Проблема группировки составного ключа SQL - PullRequest
2 голосов
/ 27 февраля 2012

У меня очень разочаровывающая проблема SQL, которую я не могу решить на протяжении всей жизни с помощью производного запроса, возвращающего составной ключ, но также выполняющего агрегатную функцию MIN () для другого поля в этой таблице.Если бы я выполнял MIN () для одного из составных ключей, это было бы легко, но, поскольку мне нужно вернуть оба ключа и выполнить функцию MIN () также для внешнего запроса, я не могу понять, как это сделать.,Весь запрос выглядит так:

SELECT
    p.name as productname
   ,tmp.packageid
   ,tmp.price
   ,ppk2.packageoptionid
   ,ppk2.selcomproductid
FROM ( 
        SELECT ppk.productid, ppk.packageid, MIN(ppk.price) as price
        FROM  product_package ppk
                 INNER JOIN package pk ON ppk.packageid = pk.id
                 INNER JOIN [plan] pl ON pk.planid = pl.id
        WHERE pk.networkid = 1
        GROUP BY ppk.productid, ppk.packageid
) tmp
INNER JOIN product_package ppk2 ON ( 
        ppk2.productid = tmp.productid 
    AND ppk2.packageid = tmp.packageid
)    
INNER JOIN product p ON (p.id = ppk2.productid)  
WHERE p.isenabled = 1;

Текущие результаты:

--------------------------------------
productid   |   packageid   |   price
1               500             0
1               501             19.95
1               502             29.95
2               501             0
3               500             15    
3               504             39.95 

Желаемые результаты:

--------------------------------------
productid   |   packageid   |   price
1               500             0
2               501             0
3               500             15  

В производном запросе "tmp" моя проблемалежит, так как мне нужны уникальные строки назад для каждой комбинации продукта / пакета с самой низкой ценой, прежде чем присоединиться к внешним таблицам.

Любая помощь будет принята с благодарностью!

Ответы [ 3 ]

2 голосов
/ 27 февраля 2012

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

Лучший способ объединения значений зависит от того, какую СУБД вы используете.Вы не упоминаете, какой из них используете, поэтому я просто предоставляю псевдокод:

select ..., (tmp.c >> 32) price
from
(select productid, min((price << 32) | packageid) c
  from product_package
  where networkid=1
  group by productid) tmp
inner join product_package ppk on ppk.productid=tmp.productid
  and ppk.packageid=(tmp.c & 0xFFFFFFFF)
inner join product p on p.id=ppk.productid
where p.isenabled=1

<< 32 означает смещение значения на 32 бита влево, а | - побитовое значение "или».Таким образом, предполагается, что packageid определен как 32-битное целое число (или число (4)).& 0xFFFFFFFF - это побитовое «и» и шестнадцатеричное значение для 32 битов, используемое для маскировки и возврата просто packageid.

В зависимости от вашей СУБД, вам может потребоваться найти ее конкретный синтаксис для этих вещей или, если они 'Вы не можете использовать обычную математику - << 32 эквивалентно умножению на 4294967296 и & 0xFFFFFFFF для деления на 4294967296. Если вы используете MSSQL, вы можете использовать convert(binary,price)+convert(binary,packageid) для их объединения и substring(..) для разделения.

1 голос
/ 27 февраля 2012

Ну, я не знаю данных, которые у вас есть в вашей таблице. У меня просто есть данные, которые возвращает ваш запрос. Вы не ответили на мой комментарий, запросив образец данных вашей таблицы и СУБД, которую вы использовали.

Однако, предполагая, что текущие данные вашей таблицы являются теми, которые поступают из вашего запроса, следующий запрос даст вам «требуемый результат», который вы указали:

select t1.* from t t1
left join t t2
on t1.productid = t2.productid and t1.details > t2.details
where t2.details is null

В табличных словах запрос превращает это:

+-----------+-----------+---------+
| PRODUCTID | PACKAGEID | DETAILS |
+-----------+-----------+---------+
|         1 |       500 |       0 |
|         1 |       501 |      20 |
|         1 |       502 |      30 |
|         2 |       501 |       0 |
|         3 |       500 |      15 |
|         3 |       504 |      40 |
+-----------+-----------+---------+

В это:

+-----------+-----------+---------+
| PRODUCTID | PACKAGEID | DETAILS |
+-----------+-----------+---------+
|         1 |       500 |       0 |
|         2 |       501 |       0 |
|         3 |       500 |      15 |
+-----------+-----------+---------+

Дайте мне знать, ясно это или нет.

1 голос
/ 27 февраля 2012

Простой (читай: дорогой) способ: создать два представления: одно, которое получает только минимум ppk.price каждого productid WHERE pk.networkid = 1, и сгруппировать его по productid.Назовите это Product_MinPrice_VIEW или как угодно.

Создайте второе представление, Product_VIEW, которое заменяет всю эту под-SELECT INNER JOIN работу, с которой вы пытаетесь уйти через INNER JOIN на результатахProduct_MinPrice_VIEW, который вы только что сделали.

Клянусь, споры с sub- SELECTS, HAVINGS и GROUP-BY s утомительны и подвержены ошибкам.Я терпеть не могу это иногда.Надеюсь, это поможет вам разработать решение, которое впоследствии можно будет оптимизировать и сделать более правильным.

ФИНАЛЬНЫЙ ОТВЕТ

У меня очень похожая проблема с приложением, над которым я работаю, и в то же время (пока я поднимаю этот сайт для лучшегоответ), я просто взял на себя ответственность и написал некоторый код уровня приложения для устранения любых дубликатов, и позволил логике программы найти истинный минимум при обнаружении.Не красиво, но опять же у меня нет целого дня, чтобы попытаться понять это!

Извините, мой ответ не смог вам помочь.Удачи!

...