Ограничить объединение одной строкой
-
11-12-2019 - |
Вопрос
У меня есть следующий запрос:
SELECT sum((select count(*) as itemCount) * "SalesOrderItems"."price") as amount, 'rma' as
"creditType", "Clients"."company" as "client", "Clients".id as "ClientId", "Rmas".*
FROM "Rmas" JOIN "EsnsRmas" on("EsnsRmas"."RmaId" = "Rmas"."id")
JOIN "Esns" on ("Esns".id = "EsnsRmas"."EsnId")
JOIN "EsnsSalesOrderItems" on("EsnsSalesOrderItems"."EsnId" = "Esns"."id" )
JOIN "SalesOrderItems" on("SalesOrderItems"."id" = "EsnsSalesOrderItems"."SalesOrderItemId")
JOIN "Clients" on("Clients"."id" = "Rmas"."ClientId" )
WHERE "Rmas"."credited"=false AND "Rmas"."verifyStatus" IS NOT null
GROUP BY "Clients".id, "Rmas".id;
Проблема в том, что таблица "EsnsSalesOrderItems"
может быть то же самое EsnId
в разных записях.Я хочу ограничить запрос, чтобы получить только последнюю запись в "EsnsSalesOrderItems"
у которого то же самое "EsnId"
.
Под «последней» записью я имею в виду следующее:
Тот, который появляется последним в таблице "EsnsSalesOrderItems"
.Так, например, если "EsnsSalesOrderItems"
имеет две записи с "EsnId" = 6
и "createdAt" = '2012-06-19'
и '2012-07-19'
соответственно, он должен дать мне только запись из '2012-07-19'
.
Решение
SELECT (count(*) * sum(s."price")) AS amount
, 'rma' AS "creditType"
, c."company" AS "client"
, c.id AS "ClientId"
, r.*
FROM "Rmas" r
JOIN "EsnsRmas" er ON er."RmaId" = r."id"
JOIN "Esns" e ON e.id = er."EsnId"
JOIN (
SELECT DISTINCT ON ("EsnId") *
FROM "EsnsSalesOrderItems"
ORDER BY "EsnId", "createdAt" DESC
) es ON es."EsnId" = e."id"
JOIN "SalesOrderItems" s ON s."id" = es."SalesOrderItemId"
JOIN "Clients" c ON c."id" = r."ClientId"
WHERE r."credited" = FALSE
AND r."verifyStatus" IS NOT NULL
GROUP BY c.id, r.id;
Ваш запрос в вопросе содержит недопустимый агрегат по сравнению с другим агрегатом:
sum((select count(*) as itemCount) * "SalesOrderItems"."price") as amount
Упрощенный и преобразованный в допустимый синтаксис:
(count(*) * sum(s."price")) AS amount
Но действительно ли вы хотите умножить число на группу?
Я получаю одну строку для каждой группы в "EsnsSalesOrderItems"
с DISTINCT ON
.Детальное объяснение:
Я также добавил псевдонимы таблиц и форматирование, чтобы упростить анализ запроса для человеческого глаза.Если бы ты мог избегайте верблюжьего случая, вы можете избавиться от всех двойных кавычек затуманивание зрения.
Другие советы
Что-то вроде:
join (
select "EsnId",
row_number() over (partition by "EsnId" order by "createdAt" desc) as rn
from "EsnsSalesOrderItems"
) t ON t."EsnId" = "Esns"."id" and rn = 1
это выберет самую последнюю "EsnId"
от "EsnsSalesOrderItems"
на основе столбца creation_date
.Поскольку вы не опубликовали структуру своих таблиц, мне пришлось «придумывать» имя столбца.Вы можете использовать любой столбец, который позволяет вам определить подходящий вам порядок в строках.
Но помните, что концепция «последней строки» действительна только в том случае, если вы указываете порядок или строки.Таблица как таковая не упорядочена и не является результатом запроса. пока не вы указываете order by
Некромантия, потому что ответы устарели.
Воспользуйтесь LATERAL
ключевое слово, введенное в PG 9.3
слева | справа | внутреннее соединение БОКОВОЙ
Поясню на примере:
Предположим, у вас есть таблица «Контакты».
Теперь у контактов есть организационные подразделения.
В определенный момент времени у них может быть одно подразделение, но в N моментов времени — N OU.
Теперь, если вам нужно запросить контакты и подразделение в период времени (не отчетная дата, а диапазон дат), вы можете увеличить количество записей в N раз, если просто выполните левое соединение.
Итак, чтобы отобразить подразделение, вам нужно просто присоединиться к первому подразделению для каждого контакта (где то, что должно быть первым, является произвольным критерием - например, при выборе последнего значения это просто еще один способ указать первое значение при сортировке). в порядке убывания даты).
В SQL-сервере вы должны использовать перекрестное применение (или, скорее, OUTER APPLY, поскольку нам нужно левое соединение), которое будет вызывать функцию с табличным значением для каждой строки, к которой она должна присоединиться.
SELECT * FROM T_Contacts
--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989
-- CROSS APPLY -- = INNER JOIN
OUTER APPLY -- = LEFT JOIN
(
SELECT TOP 1
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID
/*
AND
(
(@in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(@in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
) AS FirstOE
В PostgreSQL начиная с версии 9.3, вы тоже можете это сделать - просто используйте LATERAL
ключевое слово для достижения того же:
SELECT * FROM T_Contacts
--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989
LEFT JOIN LATERAL
(
SELECT
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID
/*
AND
(
(__in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(__in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
LIMIT 1
) AS FirstOE
Попробуйте использовать подзапрос в предложении ON.Абстрактный пример:
SELECT
*
FROM table1
JOIN table2 ON table2.id = (
SELECT id FROM table2 WHERE table2.table1_id = table1.id LIMIT 1
)
WHERE
...