Я хочу обеспечить, чтобы только одна запись в таблице считалась значением «по умолчанию» для других запросов или представлений, которые могут обращаться к этой таблице.
По сути, я хочу гарантировать, что этот запрос всегда будет возвращать ровно одну строку:
SELECT ID, Zip
FROM PostalCodes
WHERE isDefault=True
Как бы я сделал это в SQL?
mysql
database-design
emaynard
источник
источник
PostalCodes
пусто? Если у строки уже есть свойство, должно быть предотвращено присвоение ему значения false, если другая строка (если она существует) не установлена в значение true в том же операторе SQL? Могут ли нулевые строки иметь свойство между границами транзакции? Должна ли последняя строка в таблице иметь свойство и не должна быть удалена? Опыт подсказывает мне, что «гарантия ровно в один ряд» в действительности означает нечто иное, часто просто «не более одного ряда».Ответы:
Изменить: это связано с SQL Server, прежде чем мы знали MySQL
Есть
45 способов сделать это: от наиболее желаемого до наименее желаемогоМы не знаем, что это за СУРБД, хотя и не все могут применяться
Лучший способ - фильтрованный индекс. Это использует DRI для поддержания уникальности.
Вычисляемый столбец с уникальностью (см. Ответ Джека Дугласа) (добавлено редактированием 2)
Индексированное / материализованное представление, которое похоже на отфильтрованный индекс с использованием DRI
Триггер (согласно другим ответам)
Проверьте ограничение с помощью UDF. Это не безопасно для параллелизма и изоляции моментальных снимков. Смотреть один два три четыре
Обратите внимание, что требование для «одного значения по умолчанию» указано в таблице, а не в хранимой процедуре. Хранимая процедура будет иметь те же проблемы параллелизма, что и ограничение проверки с помощью udf
Примечание: много раз спрашивали об этом:
источник
Примечание. Этот ответ был дан до того, как стало ясно, что спрашивающий хочет что-то специфичное для MySQL. Этот ответ смещен в сторону SQL Server.
Примените ограничение CHECK к таблице, которая вызывает UDF и проверяет, что его возвращаемое значение <= 1. UDF может просто считать строки в таблице WHERE
isDefault = TRUE
. Это обеспечит наличие в таблице не более 1 строки по умолчанию.Вы должны добавить в столбец растровое изображение или отфильтрованный индекс
isDefault
(в зависимости от вашей платформы), чтобы этот UDF работал очень быстро.Предостережения
PostalCodes
таблицы, однако это остается проблемой производительности в тех ситуациях, когда вероятно действие на основе набора.Учитывая все эти предостережения, я рекомендую использовать предложение gbn отфильтрованного уникального индекса вместо проверочного ограничения.
источник
Вот хранимая процедура (MySQL Dialect):
Чтобы убедиться, что ваша таблица чистая и хранимая процедура работает, предполагая, что ID 200 является значением по умолчанию, выполните следующие действия:
Вместо хранимой процедуры, как насчет триггера?
Чтобы убедиться, что ваша таблица чистая и триггер работает, предполагая, что ID 200 является значением по умолчанию, выполните следующие действия:
источник
Вы можете использовать триггер, чтобы применить правила. Когда оператор UPDATE или INSERT устанавливает для isDefault значение True, SQL в триггере может установить для всех остальных строк значение False.
Вы должны учитывать другие ситуации при применении этого. Например, что должно произойти, если несколько строк и UPDATE или INSERT задают для isDefault значение True? Какие правила будут применяться, чтобы избежать нарушения правила?
Кроме того, возможно ли условие, когда нет дефолта? Что произойдет, когда обновление установит isDefault с True на False.
Как только вы определите правила, вы можете встроить их в триггер.
Затем, как указано в другом ответе, вы хотите применить проверочное ограничение, чтобы обеспечить соблюдение правил.
источник
Ниже приведены пример таблицы и данных:
Если вы создадите хранимую процедуру для установки флага IsDefault и удалите разрешения на изменение базовой таблицы, вы можете применить это с помощью запроса ниже. Это потребует сканирования таблицы каждый раз.
В зависимости от размера таблицы, индекс IsDefault (DESC) может привести к тому, что один или оба приведенных ниже запроса будут избегать полного сканирования.
Если вы не можете удалить разрешения из базовой таблицы, нужно обеспечить целостность другими средствами и использовать SQL2008, вы можете использовать отфильтрованный уникальный индекс:
источник
Для MySQL, у которого нет отфильтрованных индексов, вы можете сделать что-то вроде следующего. Он использует тот факт, что MySQL допускает использование нескольких NULL в уникальных индексах, и использует выделенную таблицу и внешний ключ для имитации типа данных, который может иметь только NULL и 1 в качестве значений.
С этим решением вместо true / false вы бы просто использовали 1 / NULL.
Думаю, единственное, что может пойти не так, это если кто-то добавит строку в
DefaultFlag
таблицу. Я думаю, что это не большая проблема, и вы всегда можете исправить это с разрешениями, если вы действительно хотите быть в безопасности.источник
По сути, это создавало вам проблемы, потому что вы поставили поле не в том месте, и все.
У вас есть таблица «По умолчанию», в которой есть PostalCode, в которой содержится идентификатор, который вы ищете. В общем, также хорошо называть ваши таблицы в соответствии с одной записью, поэтому лучше будет называть имя вашей исходной таблицы «PostalCode». По умолчанию будет таблица из одной строки, пока вам не нужно будет специализировать ваши значения по умолчанию для какой-либо другой конфигурации (например, для истории).
Последний запрос, который вы хотите, выглядит следующим образом:
источник