Почему (и как) разделить столбец, используя master..spt_values? - PullRequest
14 голосов

Подвопрос ответа на вопрос «Разделить один столбец на несколько строк» ​​, который я переписал здесь как [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

Смежный вопрос:

Ответы [ 5 ]

47 голосов
/ 25 ноября 2010

Назначение

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

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

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

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

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

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

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

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

  • Требуется набор последовательных чисел в пределах известных границ, который доступен в виде таблицы SQL, для выполнения проекции, которую должны выполнять многие системные процедуры. Тип «P» представляет собой список последовательных чисел от 0 до 2047.

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

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

Expansion

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

    SELECT *                    -- list Genders
        FROM Gender 

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

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

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

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

вы заметите 66 LockType чисел и имен в таблице поиска. Это позволяет 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'?

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

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

    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 и снова изучите этот код. Ему просто нужен список последовательных чисел, чтобы можно было пошагово проходить по списку значений в столбце CSV, побайтно. Код активируется только для каждого байта, который является запятой или концом строки.

  • Где взять набор последовательных чисел в форме таблицы SQL, а не СОЗДАТЬ одну с нуля и вставить в нее? Да, конечно, master..spt_values. Если вы знаете, что это там.

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

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

В чем выгода?

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

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

2 голосов
/ 25 ноября 2010

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

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

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

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

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

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

1 голос
/ 08 мая 2011

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

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

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

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

0 голосов
/ 05 мая 2016

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

Например, вот скрипт:

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

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

resultof splitstring

Ну вот и все.Вот ресурс от Kathi, где она сравнила производительность.

0 голосов
/ 18 августа 2012

Теперь работает нормально

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)  = '')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...