Выбор уникальных строк в наборе из двух возможностей - PullRequest
3 голосов
/ 30 сентября 2008

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

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

Хорошо, вот некоторые примеры данных, я разделил пары пустой строкой

-------------
| Key |  Col | (Together they from a Unique Pair)
--------------
|  1     Foo |
|  1     Bar |
|            |
|  2     Foo |
|            |
|  3     Bar |
|            |
|  4     Foo |
|  4     Bar |
--------------

И результат, который я ожидаю, после выполнения запроса один раз , необходимо иметь возможность выбрать этот набор результатов в одном запросе:

1 - Foo
2 - Foo
3 - Bar
4 - Foo

Первоначальное расширение:

У меня есть таблица, назовите ее TABLE, где у меня есть два столбца, скажем ID и NAME, которые вместе образуют первичный ключ таблицы. Теперь я хочу выбрать что-то, где ID=1, а затем сначала проверяет, может ли он найти строку, где NAME имеет значение «Джон», если «Джон» не существует, он должен искать строку, где NAME есть » Брюс "- но возвращает" Джон ", только если" Брюс "и" Джон "существуют или, конечно, существует только" Джон ".

Также обратите внимание, что он должен иметь возможность возвращать несколько строк в запросе, которые соответствуют вышеуказанным критериям, но, конечно, с разными комбинациями идентификаторов / имен, и что приведенное выше объяснение является лишь упрощением реальной проблемы.

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

Ответы [ 9 ]

4 голосов
/ 30 сентября 2008

Это довольно похоже на то, что вы написали, но должно быть достаточно быстрым, поскольку NOT EXISTS более эффективен, в этом случае, чем NOT IN ...

mysql> select * from foo;
+----+-----+
| id | col |
+----+-----+
|  1 | Bar | 
|  1 | Foo | 
|  2 | Foo | 
|  3 | Bar | 
|  4 | Bar | 
|  4 | Foo | 
+----+-----+

SELECT id
     , col
  FROM foo f1 
 WHERE col = 'Foo' 
  OR ( col = 'Bar' AND NOT EXISTS( SELECT * 
                                     FROM foo f2
                                    WHERE f1.id  = f2.id 
                                      AND f2.col = 'Foo' 
                                 ) 
     ); 

+----+-----+
| id | col |
+----+-----+
|  1 | Foo | 
|  2 | Foo | 
|  3 | Bar | 
|  4 | Foo | 
+----+-----+
1 голос
/ 14 октября 2010

Нет необходимости делать это слишком сложным, вы можете просто использовать MAX() и group by ...

select id, max(col) from foo group by id
1 голос
/ 30 сентября 2008

Вы можете присоединить исходную таблицу к себе с помощью ВНЕШНЕГО СОЕДИНЕНИЯ следующим образом:

create table #mytest
   (
   id           int,
   Name         varchar(20)
   );
go

insert into #mytest values (1,'Foo');
insert into #mytest values (1,'Bar');
insert into #mytest values (2,'Foo');
insert into #mytest values (3,'Bar');
insert into #mytest values (4,'Foo');
insert into #mytest values (4,'Bar');
go

select distinct
   sc.id,
   isnull(fc.Name, sc.Name) sel_name
from
   #mytest sc

   LEFT OUTER JOIN #mytest fc
      on (fc.id = sc.id
          and fc.Name = 'Foo')

вот так.

0 голосов
/ 03 октября 2008

Вот пример, который работает в SQL Server 2005 и более поздних версиях. Это полезный шаблон, в котором вы хотите выбрать верхнюю строку (или верхние n строк) на основе пользовательского порядка. Это позволит вам не просто выбирать одно из двух значений с пользовательскими приоритетами, но любое число. Вы можете использовать функцию ROW_NUMBER () и выражение CASE:

CREATE TABLE T (id int, col varchar(10));

INSERT T VALUES (1, 'Foo')
INSERT T VALUES (1, 'Bar')
INSERT T VALUES (2, 'Foo')
INSERT T VALUES (3, 'Bar')
INSERT T VALUES (4, 'Foo')
INSERT T VALUES (4, 'Bar')

SELECT id,col
FROM 
(SELECT id, col,
    ROW_NUMBER() OVER (
    PARTITION BY id 
    ORDER BY 
    CASE col 
    WHEN 'Foo' THEN 1
    WHEN 'Bar' THEN 2 
    ELSE 3 END
    ) AS RowNum 
    FROM T
) AS X
WHERE RowNum = 1
ORDER BY id
0 голосов
/ 30 сентября 2008

Хорошо, вот некоторые примеры данных, я разделил пары пустой строкой

-------------
| Key |  Col | (Together they from a Unique Pair)
--------------
|  1     Foo |
|  1     Bar |
|            |
|  2     Foo |
|            |
|  3     Bar |
|            |
|  4     Foo |
|  4     Bar |
--------------

И результат я бы ожидал:

1 - Foo
2 - Foo
3 - Bar
4 - Foo

Я решил эту проблему выше, но этот запрос ужасно неэффективен для таблиц лагера, как-нибудь иначе?

0 голосов
/ 30 сентября 2008

Я сам придумал решение, но оно довольно сложное и медленное - и оно не распространяется на более сложные запросы:

SELECT *
FROM users
WHERE name = "bruce"
OR (
    name = "john"
    AND NOT id
    IN (
        SELECT id
        FROM posts
        WHERE name = "bruce"
    )
)

Нет альтернатив без тяжелых соединений и т. Д.?

0 голосов
/ 30 сентября 2008

В PostgreSQL я считаю, что это будет так:

SELECT DISTINCT ON (id) id, name
FROM mytable
ORDER BY id, name = 'John' DESC;

Обновление - ложные сортировки перед истиной - у меня это было изначально. Обратите внимание, что DISTINCT ON - это функция PostgreSQL, а не часть стандартного SQL. Здесь происходит то, что он показывает только первую строку для любого заданного идентификатора, с которым он сталкивается. Поскольку мы упорядочиваем по погоде имя Джон, строки с именем Джон будут выбраны поверх всех других имен.

В вашем втором примере это будет:

SELECT DISTINCT ON (key) key, col
FROM mytable
ORDER BY key, col = 'Foo' DESC;

Это даст вам:

1 - Foo
2 - Foo
3 - Bar
4 - Foo
0 голосов
/ 30 сентября 2008

попробуйте это:

select top 1 * from (
SELECT 1 as num, * FROM TABLE WHERE ID = 1 AND NAME = 'John'
union 
SELECT 2 as num, * FROM TABLE WHERE ID = 1 AND NAME = 'Bruce'
) t
order by num 
0 голосов
/ 30 сентября 2008

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

SELECT f1.id
  ,f1.col
FROM foo f1 
LEFT JOIN foo f2
  ON f1.id = f2.id
  AND f2.col = 'Foo'
WHERE f1.col = 'Foo' 
  OR ( f1.col = 'Bar' AND f2.id IS NULL )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...