Как фильтровать строки на сложном фильтре - PullRequest
5 голосов
/ 10 июня 2010

У меня есть эти строки в таблице

ID  Name    Price   Delivery
==  ====    =====   ========
1   apple   1       1
2   apple   3       2
3   apple   6       3
4   apple   9       4
5   orange  4       6
6   orange  5       7

Я хочу получить цену при третьей доставке (Delivery = 3) или последнюю цену, если третьей доставки нет.

Это дало бы мне следующее:

ID  Name    Price   Delivery
==  ====    =====   ========
3   apple   6       3
6   orange  5       7

Мне не нужно полное решение, но идея того, что искать, была бы очень признательна.

Ответы [ 4 ]

4 голосов
/ 10 июня 2010
SQL> create table t (id,name,price,delivery)
  2  as
  3  select 1, 'apple', 1, 1 from dual union all
  4  select 2, 'apple', 3, 2 from dual union all
  5  select 3, 'apple', 6, 3 from dual union all
  6  select 4, 'apple', 9, 4 from dual union all
  7  select 5, 'orange', 4, 6 from dual union all
  8  select 6, 'orange', 5, 7 from dual
  9  /

Table created.

SQL> select max(id) keep (dense_rank last order by nullif(delivery,3) nulls last) id
  2       , name
  3       , max(price) keep (dense_rank last order by nullif(delivery,3) nulls last) price
  4       , max(delivery) keep (dense_rank last order by nullif(delivery,3) nulls last) delivery
  5    from t
  6   group by name
  7  /

        ID NAME        PRICE   DELIVERY
---------- ------ ---------- ----------
         3 apple           6          3
         6 orange          5          7

2 rows selected.

РЕДАКТИРОВАТЬ : Так как вам нужно «представление о том, что искать», вот описание того, почему я считаю это решение лучшим, помимо того, что это запрос с наименьшим количеством строк. Ожидаемый набор результатов означает, что вы хотите сгруппировать данные по названию фрукта («группа по имени»). И из каждой группы вы хотите сохранить значения записей с delivery = 3 или, когда этого числа не существует, последний («keep (dens_rank последний заказ по nullif (delivery, 3) null last»). В моем По моему мнению, приведенный выше запрос выглядит так: он использует только один доступ к таблице для получения результата, хотя мой запрос в этом не уникален.

С уважением, Роб.

3 голосов
/ 10 июня 2010

Используйте ROW_NUMBER дважды - один раз, чтобы отфильтровать строки, которые идут после третьей доставки, и второй раз, чтобы найти последнюю оставшуюся строку (т. Е. Типичное максимальное значение для группового запроса).

Я реализовал это, используя CTE. Я проверил это в SQL Server, но я считаю, что Oracle поддерживает тот же синтаксис.

WITH T1 AS (
    SELECT
        ID, Name, Price, Delivery,
        ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Delivery) AS rn
    FROM Table1
), T2 AS (
    SELECT
        t1.*,
        ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Delivery DESC) AS rn2
    FROM T1
    WHERE rn <= 3
)
SELECT ID, Name, Price, Delivery
FROM T2
WHERE rn2 = 1

Результат:

ID  Name    Price  Delivery
3   apple   6      3       
6   orange  5      7       
2 голосов
/ 10 июня 2010

Ответы Марка и APC работают, если вы имели в виду третью доставку, независимо от номера доставки.Вот решение, использующее аналитические функции, которые специально ищут запись с Delivery = 3.

CREATE TABLE FRUITS (
    ID NUMBER,
    Name VARCHAR2(10),
    Price INTEGER,
    Delivery INTEGER);

INSERT INTO FRUITS VALUES (1, 'apple', 1, 1);
INSERT INTO FRUITS VALUES (2, 'apple', 3, 2);
INSERT INTO FRUITS VALUES (3, 'apple', 6, 3);
INSERT INTO FRUITS VALUES (4, 'apple', 9, 4);
INSERT INTO FRUITS VALUES (5, 'orange', 4, 6);
INSERT INTO FRUITS VALUES (6, 'orange', 5, 7);
INSERT INTO FRUITS VALUES (7, 'pear', 2, 5);
INSERT INTO FRUITS VALUES (8, 'pear', 4, 6);
INSERT INTO FRUITS VALUES (9, 'pear', 6, 7);
INSERT INTO FRUITS VALUES (10, 'pear', 8, 8);

SELECT ID,
       Name,
       Price,
       Delivery
  FROM (SELECT ID,
               Name,
               Price,
               Delivery,
               SUM(CASE WHEN Delivery = 3 THEN 1 ELSE 0 END) 
                   OVER (PARTITION BY Name) AS ThreeCount,
               ROW_NUMBER()
                   OVER (PARTITION BY Name ORDER BY Delivery DESC) AS rn
          FROM FRUITS)
 WHERE (ThreeCount <> 0 AND Delivery = 3) OR
       (ThreeCount = 0 AND rn = 1)
ORDER BY ID;

DROP TABLE FRUITS;

И результаты Oracle XE 10g:

ID   Name       Price   Delivery  
---- ---------- ------- ----------
3    apple      6       3         
6    orange     5       7         
10   pear       8       8         

Я включил третий фрукт вОбразец данных, чтобы проиллюстрировать влияние различных интерпретаций вопроса.Другие решения выбрали бы ID = 9 для груши.

2 голосов
/ 10 июня 2010
select t3.ID, t3.Name, t3.Price, t3.Delivery
from  (
    select Name, max(Delivery) as MaxDelivery
    from MyTable
    group by Name
) t1
left outer join MyTable t2 on t1.Name = t2.Name and Delivery = 3
inner join MyTable t3 on t1.Name = t3.name 
    and t3.Delivery = coalesce(t2.Delivery, t1.MaxDelivery)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...