#sql #sql-server
#sql #sql-сервер
Вопрос:
У меня есть очень простая таблица MS SQL со следующими данными (с именем столбца и типом данных):
TableId PersonName Attribute AttributeValue
(int) (nvarchar 50) (nvarchar 50) (bit)
----------- ----------------------- ------------------- --------------
1 A IsHuman 1
2 A CanSpeak 1
3 A CanSee 1
4 A CanWalk 0
5 B IsHuman 1
6 B CanSpeak 1
7 B CanSee 0
8 B CanWalk 0
9 C IsHuman 0
10 C CanSpeak 1
11 C CanSee 1
12 C CanWalk 0
Теперь в результате мне нужно уникальное имя пользователя, которое имеет как атрибут isHuman, так и CanSpeak с AttributeValue = 1.
Ожидаемый результат должен быть (не должен включать C, поскольку этот имеет isHuman = 0)
PersonName
------------
A
B
Пожалуйста, может ли какой-либо эксперт помочь мне в написании SQL-запроса для этого.
Комментарии:
1. @suresh вы опубликовали свой ответ здесь, и я также отформатировал этот вопрос, и он выглядит нормально, тогда почему вы изменили все это на эту ссылку
Ответ №1:
SELECT PersonName
FROM MyTable
WHERE AttributeName = 'IsHuman'
AND AttributeValue = 1
INTERSECT
SELECT PersonName
FROM MyTable
WHERE AttributeName = 'CanSpeak'
AND AttributeValue = 1;
Очевидно, что этот подход не «масштабируется», если критерии могут меняться. Возможно, вам требуется реляционный оператор division, широко известный как «поставщик, который поставляет все детали», в частности division с остатком.
Ответ №2:
SELECT PersonName
FROM MyTable
WHERE (AttributeName = 'IsHuman' AND AttributeValue = 1) OR
(AttributeName = 'CanSpeak' AND AttributeValue = 1)
GROUP BY PersonName
HAVING COUNT(*) > 1
или
SELECT PersonName
FROM MyTable
WHERE AttributeValue = 1 AND AttributeName IN ('IsHuman', 'CanSpeak')
GROUP BY PersonName
HAVING COUNT(*) > 1
Комментарии:
1. он должен иметь точно такой же план выполнения, что и ваша исходная версия, но я нахожу его немного более читаемым. 1 кстати о вашем решении.
2. Привет, Дилан Смит, спасибо за быстрый ответ. Можем ли мы добавить больше в предложение where, например «CanSee», «CanWalk» и т. Д. В соответствии с выбором пользователя. Это может варьироваться в зависимости от выбора.
3. @sureshjat: похоже, вам нужно взглянуть на реляционный оператор деления , широко известный как «поставщик, который поставляет все детали» , в частности деление с остатком .
4. Конечно, просто добавьте его в предложение IN и соответствующим образом увеличьте число в предложении HAVING
5. Оба решения будут работать неправильно, если (например) будут две записи с attributeName = ‘isHuman’ и AttributeValue = 1
Ответ №3:
SELECT PersonName FROM MyTable
WHERE PersonName IN
(SELECT T1.PersonName FROM MyTable T1 WHERE T1.Attribute = 'IsHuman' and T1.AttributeValue='1')
AND (Attribute = 'CanSpeak' AND AttributeValue='1')
Комментарии:
1. Это не сработает только потому, что я использовал неправильный тип AttributeValue. Я не вижу другой проблемы. Объясните это, пожалуйста.
2. Я пропустил закрывающие скобки вашего оператора IN и прочитал это как то, что предложение CanSpeak было дополнено предложением isHuman. Я удалю свой комментарий и поддержу этот ответ, но вы могли бы подумать о переформатировании инструкции, чтобы было очевидно, что AND НЕ является частью инструкции IN.
Ответ №4:
Я думаю, что два внутренних соединения могут обеспечить вам хорошую производительность в зависимости от индексации и размеров таблицы.
SELECT t.PersonName FROM table t
INNER JOIN table t2 ON t.PersonName=t2.PersonName AND t3.Attribute = 'IsHuman' AND t2.AttributeValue = 1
INNER JOIN table t3 ON t2.PersonName=t3.PersonName AND t3.Attribute = 'CanSpeak' AND t3.AttributeValue = 1
или
SELECT t.PersonName FROM table t
INNER JOIN table t2 ON t.PersonName=t2.PersonName
INNER JOIN table t3 ON t2.PersonName=t3.PersonName
WHERE t2.Attribute = 'IsHuman' AND t2.AttributeValue = 1 AND t3.Attribute = 'CanSpeak' AND t3.AttributeValue = 1
Однако это решение можно было бы значительно упростить, если бы свойства isHuman и CanSpeak находились в отдельных таблицах со связующей таблицей идентификаторов между ними. Похоже, что эта таблица могла бы выиграть от некоторой нормализации.
Если вы не можете выполнить это, представление может помочь в производительности. Я дома без установленного SQL, поэтому я не могу проверить какие-либо аспекты производительности.
Ответ №5:
select personname from yourtablename where personname in ('a','b') group by personname
Комментарии:
1. Это дает ожидаемый результат … 🙂
2. Привет, Дилан Смит, можем ли мы добавить больше в предложение where, например «CanSee», «CanWalk» и т. Д. В соответствии с выбором пользователя. Он может меняться в зависимости от выбора.
Ответ №6:
На самом деле я использую это как проверочный вопрос для интервью. Никто из вас, люди, не получил бы работу.
Хорошо, возможно, вы бы так и сделали, но хотя используемые вами стратегии могут работать, а могут и не работать, они не поддаются обобщению и в них отсутствует базовое понятие реляционной алгебры, а именно псевдонимирование.
Правильный ответ (в том смысле, что это повысит вероятность того, что я найму вас, а также уменьшит важность того, что оптимизатор RDMS понимает это, и его можно распространить на другие, произвольно сложные случаи):
SELECT t1.PersonName
FROM MyTable t1, MyTable t2
WHERE t2.AttributeName = 'CanSpeak'
AND t2.AttributeValue = 1
AND t1.AttributeName = 'IsHuman'
AND t1.AttributeValue = 1
AND t1.PersonName = t2.PersonName;
Комментарии:
1. Это утверждение содержит ошибку, использует антишаблон и работает хуже, чем предлагаемые решения. Вы должны уволить любого, кто предлагает этот <vbg> (не мой downvote кстати)
2. Может быть расширен путем добавления нового табличного выражения для каждой строки критериев поиска, что непрактично. Хотя я согласен, что его можно распространить на сколь угодно сложные случаи, достичь этого нелегко. «Правильный» способ сделать это — использовать отношения изображений. В SQL лучшее, что мы можем получить, — это реляционное разделение. Помните, что в SQL единицей работы является строка (а не таблица): способ расширения в этом случае — добавить строки критериев в таблицу делителей и использовать для каждой из них один и тот же запрос. Ваше предложение по сути является динамическим SQL.
3. Кстати, оператор переименования отношения, который вы называете «псевдонимом» (а стандарты SQL называют «именем корреляции»), не является частью какой-либо известной мне реляционной алгебры. Конечно, это полезно, но недостаточно важно для алгебры. Алгебра Дарвена включает переименование атрибута, но не переименование отношения. Действительно, Дарвен особенно критически относится к тому факту, что SQL заставляет без необходимости применять имя корреляции к таблице. Цитата : страница 9,
TEETH_GNASHER
.4. @Lieven — ваш комментарий содержит неуместность, неопровержимое утверждение и апелляцию к авторитету.
5. @onedaywhen — если бы критериям поиска было разрешено меняться, тогда да, вы не могли бы использовать этот метод. Предполагая, что критерии были исключительно объединением случаев, тогда разделение, как вы упоминаете в своих комментариях к OP, было бы правильным решением, но реализует ли какой-либо SQL разделение? Я отказался от того, чтобы называть это «именем корреляции», потому что многие документы RDBMS называют это «псевдонимом», но «переименовать» просто вводит в заблуждение: это представление в таблице, и я называю его, а не переименовываю. Среди многих, многих недостатков SQL является то, что иногда он излишне требует имен корреляции, но это не один из тех случаев.