У меня есть 2 таблицы без идентичного первичного ключа между ними (id существуют в обеих, первичные только для таблицы A). Я хочу использовать первичный ключ первой таблицы A для предложения ON. Поэтому у меня будут дубликаты из второй таблицы B. Я хочу GROUP BY дубликатов на основе некоторого поля B.cnt и всегда беру первое - DESC LIMIT 1.

Вот что я попробовал (СУБД - PostgreSQL):

SELECT 
    scheme1.A.some_attr, 
    B.some_attr
FROM 
    (SELECT * FROM scheme2.B ORDER BY scheme2.B.cnt DESC LIMIT 1) AS B
INNER JOIN
    scheme1.A
ON
    scheme1.A.id = B.id
;

Запрос возвращает одну запись. Хотя желаемое поведение - возвращать одну запись только для каждого набора записей из B, имеющих одинаковые id (на основе упомянутых критериев). Таким образом, всего запрос, конечно, вернет несколько записей ...

Как мне достичь желаемого результата?

Спасибо

0
michael 28 Май 2017 в 11:11

2 ответа

Лучший ответ

Используйте аналитическую функцию окна rank (), см. Функции окна Postgres

SELECT * FROM (
                SELECT          
                scheme1.A.some_attr, 
                scheme2.B.some_attr,                     
                rank() OVER (PARTITION BY B.ID ORDER BY scheme2.B.cnt, scheme2.B.another_attr DESC) as rnk
                FROM 
                                scheme1.A
                INNER JOIN
                                scheme2.B
                ON
                                scheme2.B.id = scheme2.A.id
) A WHERE rnk = 1;
1
dovka 28 Май 2017 в 12:17

Ваша проблема должна быть в этой строке:

(SELECT * FROM scheme2.B ORDER BY scheme2.B.cnt DESC LIMIT 1) scheme2.B

Это лечится следующим образом:

(SELECT * FROM scheme2.B ORDER BY scheme2.B.cnt DESC LIMIT 1) AS scheme2.B

Там, где псевдоним scheme.B явно неверен, измените его следующим образом, и он должен работать

SELECT 
    scheme1.A.some_attr, 
    scheme2.B.some_attr
FROM 
    (SELECT * FROM scheme2.B ORDER BY scheme2.B.cnt DESC LIMIT 1) AS B
INNER JOIN
    scheme1.A
ON
    scheme1.A.id = B.id
;

РЕДАКТИРОВАТЬ:

SELECT 
    scheme1.A.some_attr, 
    scheme2.B.some_attr
FROM 
    scheme1.A
LEFT JOIN LATERAL
    (SELECT * FROM scheme2.B WHERE scheme2.B.id = scheme2.A.id ORDER BY scheme2.B.cnt DESC LIMIT 1) AS B ON TRUE
;

Если это один атрибут, вы можете сделать следующее:

SELECT 
    scheme1.A.some_attr, 
    (
        SELECT 
            scheme2.B.some_attr
        FROM
            scheme2.B
        WHERE
            scheme2.B.id = scheme2.A.id
        ORDER BY scheme2.B.cnt DESC LIMIT 1
    )
FROM 
    scheme1.A
;
1
Evaldas Buinauskas 28 Май 2017 в 19:03