Сравните два отношения в SQL - PullRequest
1 голос
/ 04 мая 2020

Я только начал изучать SQL, и это демо, данное учителем в онлайн-курсе, и оно отлично работает. В заявлении ищутся слова «студенты, у которых число других студентов с таким же средним баллом равно количеству студентов с таким же размером HS»:

select *
from Student S1
where (
    select count(*)
    from Student S2
    where S2.sID <> S1.sID and S2.GPA = S1.GPA
) = (
    select count(*)
    from Student S2
    where S2.sID <> S1.sID and S2.sizeHS = S1.sizeHS
);

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

Так что я размышляя о , существуют ли требования к количеству атрибутов и количеству кортежей, ОТНОШЕНИЕ должно содержать при сравнении двух ОТНОШЕНИЙ . Если нет, как мы можем сравнить два ОТНОШЕНИЯ, когда имеется несколько атрибутов или несколько кортежей, и что мы получаем для результата ? Примечание :

Отношение ученика имеет 4 атрибута: sID, sName, GPA, sizeHS . А вот data :

+-----+--------+-----+--------+
| sID | sName  | GPA | sizeHS |
+-----+--------+-----+--------+
| 123 | Amy    | 3.9 |   1000 |
| 234 | Bob    | 3.6 |   1500 |
| 345 | Craig  | 3.5 |    500 |
| 456 | Doris  | 3.9 |   1000 |
| 567 | Edward | 2.9 |   2000 |
| 678 | Fay    | 3.8 |    200 |
| 789 | Gary   | 3.4 |    800 |
| 987 | Helen  | 3.7 |    800 |
| 876 | Irene  | 3.9 |    400 |
| 765 | Jay    | 2.9 |   1500 |
| 654 | Amy    | 3.9 |   1000 |
| 543 | Craig  | 3.4 |   2000 |
+-----+--------+-----+--------+

и результат этого запроса:

+-----+--------+-----+---------+
| sID | sName  | GPA | sizeHS  |
+-----+--------+-----+---------+
| 345 | Craig  | 3.5 |     500 |
| 567 | Edward | 2.9 |    2000 |
| 678 | Fay    | 3.8 |     200 |
| 789 | Gary   | 3.4 |     800 |
| 765 | Jay    | 2.9 |    1500 |
| 543 | Craig  | 3.4 |    2000 |
+-----+--------+-----+---------+

Ответы [ 2 ]

1 голос
/ 04 мая 2020

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

Отношение - это научное c имя для того, что мы называем таблицей в базе данных, и мне нравится Название «таблица» намного лучше, чем «отношение». Стол легко представить. Мы знаем их из нашего школьного расписания, например. Да, мы связываем вещи здесь внутри таблицы (день и время и предмет, преподаваемый в школе), но мы также можем связать таблицы с таблицами (расписание учеников с таблицей класса комнаты, общий предметный график и расписание учителя). Таким образом, таблицы в СУБД также связаны друг с другом (отсюда и название реляционная система управления базами данных). Я нахожу отношение имени для таблицы довольно запутанным (и многие люди используют вместо этого слово «отношение» для описания отношений между таблицами).

Итак, да, сам результат запроса опять таблица («отношение»). И, конечно, из таблиц мы можем выбрать:

select * from (select * from b) as subq;

И затем есть скалярные запросы, которые возвращают ровно одну строку и один столбец. select count(*) from b такой запрос. Хотя это все еще таблица, которую мы можем выбрать из

select * from (select count(*) as cnt from b) as subq;

, мы даже можем использовать их там, где у нас обычно есть отдельные значения, например, в предложении select:

select a.*, (select count(*) from b) as cnt from a;

В вашем запросе вы У вас есть два скалярных подзапроса в вашем предложении where.

С подзапросами нужно сделать еще одно различие: у нас есть коррелированные и некоррелированные подзапросы. Последний запрос, который я только что показал, содержит некоррелированный подзапрос. Он выбирает количество b строк для каждой строки результата, независимо от того, что эта строка содержит в противном случае. С другой стороны, коррелированный подзапрос может выглядеть так:

select a.*, (select count(*) from b where b.x = a.y) as cnt from a;

Здесь подзапрос связан с основной таблицей. Для каждой строки результата мы ищем количество строк b, соответствующих строке, которую мы отображаем, с помощью where b.x = a.y, поэтому число отличается от строки к строке (но мы получили бы одинаковое количество строк, имеющих одинаковое значение y ).

Ваши подзапросы также коррелированы. Как и в случае с предложением select, предложение where имеет дело с одной строкой за раз (для ее сохранения или отклонения). Итак, мы смотрим на одного студента S1 одновременно. Для этого студента мы учитываем других студентов (S2, where S2.sID <> S1.sID), которые имеют тот же средний балл (and S2.GPA = S1.GPA), и считаем других студентов, имеющих тот же размер HS. Мы оставляем учеников (S1) там, где есть ровно столько же учеников с тем же GPA, сколько и с таким же размером HS.


ОБНОВЛЕНИЕ

Как и в случае с несколькими кортежами: в

select *
from Student S1
where (
    select count(*), avg(grade)
    from Student S2
    where S2.sID <> S1.sID and S2.GPA = S1.GPA
) = (
    select count(*), avg(grade)
    from Student S2
    where S2.sID <> S1.sID and S2.sizeHS = S1.sizeHS
);

это возможно в некоторых СУБД, но не в SQL Server. SQL Сервер не знает кортежей.

Но есть и другие способы добиться того же. Вы можете просто добавить два подзапроса:

select * from student s1
where (...) = (...) -- compare counts here
and (...) = (...) -- compare averages here

или получить данные в предложении FROM, а затем обработать их. Например:

select *
from Student S1
cross apply
(
    select count(*) as cnt, avg(grade) as avg_grade
    from Student S2
    where S2.sID <> S1.sID and S2.GPA = S1.GPA
) sx
cross apply
(
    select count(*) as cnt, avg(grade) as avg_grade
    from Student S2
    where S2.sID <> S1.sID and S2.sizeHS = S1.sizeHS
) sy
where sx.cnt = sy.cnt and sx.avg_grade = sy.avg_grade;
0 голосов
/ 04 мая 2020

Существуют реляционные операции :

  • Оператор пересечения создает набор кортежей, общих для двух отношений. Пересечение реализовано в SQL в форме оператора INTERSECT.
  • Оператор разности действует на два отношения и создает набор кортежей из первого отношения, которые не существуют во втором отношении. Разница реализована в SQL в форме оператора EXCEPT или MINUS.

Так, например, в контексте SQL Server вы можете выполнить:

SELECT *
FROM R1 
EXCEPT
SELECT *
FROM R2 

для получения строк в R1, не включенных в R2, и наоборот - для получения всех различий.

Конечно, атрибуты должны быть одинаковыми - если нет, вам нужно явно установить атрибуты в SELECT.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...