Почему (и как) разделять столбцы с использованием master..spt_values?

Подтверждая ответ на вопрос "Разделить один столбец на несколько строк" , который я переписал здесь как [1].

Что такое (значение) Type = 'P' и почему использовать недокументированные master..spt_values ​​для разделения столбца? Какая польза от этого?


[1]

CREATE TABLE dbo.Table1 
(
    Col1        CHAR(1),
    Col2        CHAR(1),
    Col3        CHAR(1),
    Col4        VARCHAR(50)
)
GO

INSERT INTO dbo.Table1 VALUES ('A','B','C','1,2,3')
GO
INSERT INTO dbo.Table1 VALUES ('D','E','F','6,7,8,9')
GO


SELECT
    T.col1, RIGHT(LEFT(T.col4,Number-1),
    CHARINDEX(',',REVERSE(LEFT(','+T.col4,Number-1))))
FROM
    master..spt_values,
    table1 T
WHERE
    Type = 'P' AND Number BETWEEN 1 AND LEN(T.col4)+1 AND
    (SUBSTRING(T.col4,Number,1) = ','
    -- OR SUBSTRING(T.col4,Number,1)  = '') --this does not work correctly anyway

Похожие вопросы:

Ответ 1

Цель

Зачем использовать недокументированные master..spt-values

Sybase и, следовательно, его сын-ублюдок MS SQL, предоставляют различные функции и функции для продукта, которые реализуются в системных процедурах (в отличие от двоичных файлов, таких как sqlserver, которые запускаются как служба). Эти процедуры процедур системы написаны в коде SQL и называются sp_%. За исключением некоторых секретных внутренних элементов, они имеют те же ограничения и потребности, что и любой другой код SQL. Они являются частью продукта Sybase ASE или SQL Server. Таким образом, они не обязаны документировать его; и внутренние биты не могут быть разумно помечены как "недокументированные".

master..spt_values содержит все различные биты и куски, которые требуются указанным системным процедурам в таблице SQL, для создания различных отчетов. sp означает системную процедуру; spt означает таблицы для системных процедур; и, конечно, values - это контент.

Таблицы поиска

Что такое (значение) Type = 'P'

Люди часто описывают spt_values как "ненормированные", но это неправильный термин. Правильный термин складывается или упаковывается. Это 26 или около того логических таблиц Lookup, каждая из которых красиво Normalized, сложенная в одну физическую таблицу, с столбцом Type, чтобы различать логические таблицы.

Теперь в нормальной базе данных это будет грубая ошибка (просто посмотрите ответы на "одну таблицу поиска или много" ). Но в каталоге сервера, желательно, он заменяет 26 физических таблиц.

  • "L" означает для поиска LockType; "V" означает "Поиск по DeviceType" (V является коротким для устройства на всем сервере); и т.д. Тип "P2" содержит побитовые ординалы, для расширения бит, которые упакованы в INT.

  • Требуется набор последовательных чисел в пределах известных границ, доступных в форме таблицы SQL, для выполнения Проекции, которые должны выполняться многими системными процедурами. Тип "P" - это список последовательных чисел от 0 до 2047.

  • Термин Проекция используется здесь как технически точный смысл, естественный логический смысл, а не значение реляционной алгебры, что является неестественным.

Таким образом, для spt_values, существует только одна цель: содержать 26 сложенных, иначе разделенных, справочных таблиц и одну таблицу проекций.

Расширение

Обычное использование spt_values тогда, как обычная таблица поиска или ссылки или ENUM. Во-первых, значения Lookup:

    SELECT *                    -- list Genders
        FROM Gender 

Он используется таким же образом, что Person имеет гендерный код, который необходимо развернуть (очень расширены, эти причудливые дни):

    SELECT  P.*,                -- list Person
            G.Name              -- expand GenderCode to Name
        FROM Person P
        JOIN Gender G
            ON P.GenderCode = G.GenderCode

Eg. sp_lock создает отчет о активных блокировках, отображая типы блокировки в виде имен строк. Но master..syslocks содержит типы блокировок как числа, он не содержит этих имен; и если бы это было так, это был бы плохо денормализованный стол! Если вы выполните запрос (код Sybase ASE, вам придется преобразовать):

    SELECT *                    -- list LockTypes
        FROM master..spt_values 
        WHERE type = "L"

вы увидите 66 номеров и имен LockType в таблице Lookup. Это позволяет sp_lock выполнять простой код наподобие Person:: Gender выше:

    SELECT  spid,               -- list Active Locks
            DB_NAME(dbid),
            OBJECT_NAME(id, dbid),
            v.name,             -- expand lock name
            page,
            row
    FROM master..syslocks   L,
         master..spt_values LT
    WHERE L.type = LT.number    -- 
    AND   type = "L"            -- LockType Lookup table
    ORDER by 1, 2, 3, 4, 5, 6   -- such that perusal is easy

Проекция

Что такое (значение) Type = 'P'?

Что такое Projection и как оно используется?

Скажем, например, вместо активных блокировок, созданных запросом выше, вам нужен список всех 66 LockTypes, показывающий количество активных блокировок (или Null). Вам не нужен курсор или цикл WHILE. Мы можем спроектировать таблицу LookType Lookup, подсчитывая количество активных блокировок:

    SELECT  LT.name,            -- list LockTypes
            [Count] = (         -- with count
        SELECT COUNT(*)
            FROM master..syslocks
            WHERE type = LT.number
                )
        FROM master..spt_values LT
        WHERE type = "L"

Существует несколько методов, то есть один. Другой метод - использовать производную таблицу вместо подзапроса. Но вам все равно нужна проекция.

Это обычно означает, что spt_values используется для расширения или проецирования. Теперь, когда вы знаете, что он есть, вы тоже можете его использовать. Он безопасен (в базе данных master) и используется практически всеми системными процедурами, что означает, что системные процедуры не могут работать без него.

для разделения столбца?

А, вы не понимаете код "Разделить один столбец CSV на несколько строк".

  • Забудьте о spt_values на мгновение и снова проверьте этот код. Ему просто нужен список последовательных номеров, так что in может пройти через список значений в столбце CSV, байт по байту. Код активируется только для каждого байта, который представляет собой запятую или конец строки.

  • Где получить набор последовательных чисел в виде таблицы SQL, а не CREATing один с нуля и INSERTING? Почему, master..spt_values, конечно. Если вы знаете, что он есть.

  • (Вы можете немного узнать о внутренних компонентах ASE или SQL Server, просто прочитав код хранимых процедур системы.)

  • Обратите внимание, что любое поле CSV в одном столбце является грубой ошибкой нормализации, оно разбивает 2NF (содержит повторяющиеся значения) и 1NF (не является атомом). Обратите внимание, что это не упакован или не сложен, это повторяющаяся группа, она не нормирована. Одним из многих негативных последствий такой грубой ошибки является использование простого SQL для перемещения повторяющейся группы в виде строк, поэтому для определения и извлечения содержимого ненормированного поля CSV необходимо использовать сложный код. Здесь spt_values P предоставляет вектор для этого сложного кода, облегчая его.

Какая польза от этого?

Думаю, я ответил на это. Если у вас его нет, каждая системная процедура, требующая списка номеров, должна будет СОЗДАТЬ временную таблицу; и ВСТАВИТЬ строки в него; перед запуском его кода. Конечно, не нужно выполнять эти шаги, делает процедуры системы намного быстрее.

Теперь, когда вам нужно выполнить проецирование, например. даты календаря в будущем или что-то еще, вы можете использовать spt_values вместо того, чтобы каждый раз создавать свою собственную временную таблицу (или создавать свою собственную постоянную таблицу и поддерживать ее).

Ответ 2

Многие общие решения для разделения строк в TSQL требуют списка чисел; в этом случае кто-то использует таблицу spt_values ​​для их предоставления. При проверке этот запрос возвращает список из 2048 последовательных целых чисел:

select number from master..spt_values where type = 'P'

Я предполагаю, что исходный писатель запросов использовал spt_values ​​для целых чисел, потому что a) он "гарантирован" доступен, и поэтому запрос всегда будет работать, и b) он избегает подробного объяснения альтернативных способов получения целых чисел.

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

Существует множество альтернативных способов получения списка чисел без использования недокументированных таблиц (я использую функцию, учитывающую таблицу):

SQL, вспомогательная таблица чисел

Ответ 3

Я знаю, что это более старое сообщение, но я подумал, что добавлю обновление. Таблица Tally Table и cteTally, основанная на таблице, имеют серьезную проблему. Они используют конкатенированные разделители и убивают их скорость, когда элементы становятся шире, а строки становятся длиннее.

Я исправил эту проблему и написал статью об этом, которая может быть найдена по следующему URL. http://www.sqlservercentral.com/articles/Tally+Table/72993/

Новый метод удаляет двери из всех методов While Loop, Recursive CTE и XML для VARCHAR (8000).

Я также скажу вам, что парень по имени "Питер" сделал улучшение даже в этом коде (в обсуждении статьи). Статья по-прежнему интересна, и я буду обновлять вложения с улучшениями Peter на следующий день или два. Между моим главным улучшением и tweek, сделанным Peter, я не верю, что вы найдете более быстрое решение T-SQL для разделения VARCHAR (8000). Я также решил проблему для этой породы разветкителей для VARCHAR (MAX), и я тоже в процессе написания статьи.

Ответ 4

Теперь он отлично работает

SELECT T.col1, RIGHT(LEFT(T.col4,Number-1),CHARINDEX(',',REVERSE(LEFT(','+T.col4,Number-1))))
FROM
    master..spt_values,
    table123 T
WHERE
    Type = 'P' AND Number BETWEEN 1 AND LEN(T.col4)+1 AND
    (SUBSTRING(T.col4,Number,1) = ','
  OR SUBSTRING(T.col4,Number,1)  = '')

Ответ 5

В SQL Server 2016 теперь появилась новая функциональность String_Split, которую мы можем использовать для разделения столбцов.

Например, здесь находится script:

DECLARE @String NVARCHAR(1000) = 'abc,def,ghi,jkl,mno,pqr,stu,vw,xyz';
SELECT * FROM STRING_SPLIT(@String,',');

После выполнения выше script он вернет нам следующий результат.

resultof splitstring

Хорошо, что это. Здесь ресурс от Kathi, где она сравнила производительность.