Выбор уникальных строк в наборе из двух возможностей

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

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

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

-------------
| 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 имеет значение «John», если «John» не существует, он должен искать строку, где NAME является «Bruce» - но только вернуть «Джон», если существуют и «Брюс», и «Джон», или, конечно, существует только «Джон».

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

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

Не могли бы вы прояснить этот вопрос на примере? т.е. предоставление минимальных наборов данных и решений, которые вы хотите получить на их основе?

Georgi 30.09.2008 01:00

Насколько кроссплатформенным должен быть ответ? Я мог бы придумать что-то, что работало бы в SQL Server, но не в MySQL ...

Tom Ritter 30.09.2008 01:01

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

thr 30.09.2008 01:22
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
3
3
3 469
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

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

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 )

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

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 

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

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')

как это.

Кажется, я получаю сообщение об ошибке при попытке этого в mysql, только postgre?

thr 30.09.2008 01:23

Я тестировал его в SQL Server 2005.

Ron Savage 30.09.2008 01:30

В PostgreSQL, я думаю, это было бы так:

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

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

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

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

Это даст вам:

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

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

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

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

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

-------------
| 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

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

Ответ принят как подходящий

Это довольно похоже на то, что вы написали, но должно быть довольно быстрым, поскольку 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 | 
+----+-----+

Вот пример, который работает в 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

Не нужно усложнять этот процесс, можно просто использовать MAX() и group by ....

select id, max(col) from foo group by id

Другие вопросы по теме