SQL: как найти минимальное количество записей, связанных из одной таблицы в другую

допустим, у меня есть Table_A:

A_id | A_val
1      a
2      b
3      c

Некоторые Table_B:

B_id | B_val
1      d
2      e
3      g

и компоновщик Table_C:

A_id | B_id
1      1
2      1
2      2
3      1
3      2
3      3

мне нужна помощь, чтобы найти элементы!--11-->Таблица A, который имеет наименьшее количество элементов в Таблица B связаны с ним. В настоящее время я новичок в SQL с использованием PostgreSQL и решил, что это может иметь какое-то отношение к использованию подзапроса. Мне удалось подсчитать ссылки, используя:

SELECT A_id, COUNT(B_id) as Num_links
  FROM TABLE_C
  GROUP BY A_id;

но Я понятия не имею, куда идти дальше.

6 ответов


вы могли бы использовать with предложение, чтобы дать псевдоним вашему запросу "count" и рассматривать его как временную таблицу. Выберите a_id С num_links меньше-чем-или-равно-до самого низкого рассчитывать в num_links.

WITH link_counts AS (
         SELECT a_id, COUNT(b_id) as num_links
           FROM table_c
       GROUP BY a_id
     )
SELECT a_id
  FROM link_counts
 WHERE num_links <= (SELECT MIN(num_links) FROM link_counts)

обратите внимание, что это может вернуть несколько строк, если разные a_id имеют одинаковое (наименьшее) количество ссылок (например, если a_id 1 и 4 оба имели только по 1 ссылке).


можно использовать RANK(). Это будет ранжировать вашу помощь по COUNT(Bid) -- для тех, у кого одинаковое число, все будут возвращены с одинаковым рангом.

SELECT *
FROM A T1
  JOIN (
    SELECT Aid, RANK() OVER (ORDER BY COUNT(Bid)) rnk
    FROM C 
    GROUP BY Aid
    ) T2 ON T1.Id = T2.Aid
WHERE T2.rnk = 1

и вот это Скрипка.

удачи.


вот еще один вариант. Он использует подзапрос в HAVING статья:

SELECT DISTINCT AId, COUNT(*)
FROM C
GROUP BY AId
HAVING COUNT(*) <= ALL (SELECT COUNT(*) 
                        FROM C 
                        GROUP BY AId)

и связанные с скрипка. Я понятия не имею, как это будет сравниваться с другими решениями с точки зрения производительности, но, похоже, это ясно показывает, что происходит.


вот стратегия. Рассчитайте максимальное количество ссылок. Вы можете сделать это, изменив свой запрос с помощью order by и limit.

Далее следует рассчитать общее количество ссылок для каждой строки tableC. Для этого я использую функцию window. Заявление:

count(*) over (partition by a_id)

говорит создать переменную, которая является количеством " a " s в моей таблице.

тогда соедините это вместе.

select distinct c.a_id
from (select c.*,
             count(*) over (partition by a_id) as num_links
      from table_c c
     ) c join
     (select a_id, count(*) as num_links
      from table_c c
      group by a_id
      order by 2 asc
      limit 1
     ) cmax
     on c.num_links = cmax.num_links

WITH ct AS (
   SELECT a.a_id
         ,count(c.a_id) AS link_ct
         ,min(count(c.a_id)) OVER () AS min_ct
   FROM   table_a a
   LEFT   JOIN table_c c USING (a_id)
   GROUP  BY 1
   )
SELECT a_id, link_ct
FROM   ct
WHERE  link_ct = min_ct;

это похоже на то, что @ matts posted. Он отличается в некоторых аспектах:

  • в CTE ct Я считаю LEFT JOIN to table_c, таким образом я не пропускаю строки table_a С 0 соединения to table_b который должен выиграть в соответствии с определением в вопросе.
  • вычислить min_ct в CTE с оконной функцией (и, следовательно, без дополнительного подзапроса в final WHERE состояние). Может или не может быстрее, в любом случае чище.
  • финал WHERE состояние хорошее с = вместо <=.

->sqlfiddle демонстрируя разницу.


похоже, что у других здесь есть более элегантное решение... мой SQL Fu немного ржавый, но это тоже сработает.


CREATE TABLE Table_C
(
    A_id INT,
    B_id INT
);

INSERT INTO Table_C (A_id, B_id) VALUES (13, 112);
INSERT INTO Table_C (A_id, B_id) VALUES (44, 105);
INSERT INTO Table_C (A_id, B_id) VALUES (66, 68);
INSERT INTO Table_C (A_id, B_id) VALUES (13, 113);
INSERT INTO Table_C (A_id, B_id) VALUES (445, 105);
INSERT INTO Table_C (A_id, B_id) VALUES (660, 68);

CREATE TABLE TempTable
(
    A_id INT,
    Cnt INT
);

INSERT INTO
  TempTable (A_id, Cnt)
SELECT
  t.A_id
  , COUNT(t.A_id) AS Cnt
FROM
  Table_C t
GROUP BY
  t.A_id;

SELECT @minCnt := MIN(Cnt) FROM TempTable;

SELECT
  A_id
FROM
  Table_C
GROUP BY
  A_id
HAVING
  COUNT(A_id) = @minCnt;