2016-06-08 4 views
1

Я хотел бы создать динамический выбор, который возвращает каждое отдельное значение для каждого столбца в широкой таблице. То естьКаждое отдельное значение для каждого столбца

select distinct @mycolumn 
from @mytable 

для каждой колонки и результаты объединены в одну таблицу.

Edit1:

Пример: Example

Edit2: Порядок возвращаемых данных не имеет значения, и исходная таблица может иметь все виды типов данных.

Любые советы приветствуются, спасибо!

+2

Не будут отличаться типы данных ваших столбцов? – Heinzi

+3

Показать пример данных и желаемых результатов. –

+2

Я не могу представить ни одного сценария, где это было бы полезно. Как бы вы все равно их объединили? Я предполагаю, что каждый столбец имеет различное количество различных значений .... –

ответ

2

Единственный способ, о котором я могу думать, очень громоздкий и, возможно, очень медленный: Использование таблицы Tally (я создал один, используя рекурсивный cte для этого ответа, но это также не очень хороший способ сделайте это ...), и несколько полученных таблиц слева присоединились к этой таблице таблиц, я смог придумать что-то, что будет генерировать желаемый результат.
Однако, как я писал сверху, это очень громоздко и, вероятно, очень медленно (я тестировал только на столе с 5 столбцами и 6 строками, поэтому я понятия не имею о скорости выполнения).

DECLARE @Count int 
select @Count = COUNT(1) 
FROM YourTable 

;with tally as (
    select 1 as n 
    union all 
    select n + 1 
    from tally 
    where n < @Count 
) 

SELECT Column1, Column2, Column3, Column4, Column5 
FROM tally 
LEFT JOIN 
(
    SELECT Column1, ROW_NUMBER() OVER (ORDER BY Column1) rn 
    FROM 
    (
     SELECT DISTINCT Column1 
     FROM YourTable 
    ) t1 
) d1 ON(n = d1.rn) 
LEFT JOIN 
(
    SELECT Column2, ROW_NUMBER() OVER (ORDER BY Column2) rn 
    FROM 
    (
     SELECT DISTINCT Column2 
     FROM YourTable 
    ) t1 
) d2 ON(n = d2.rn) 
LEFT JOIN 
(
    SELECT Column3, ROW_NUMBER() OVER (ORDER BY Column3) rn 
    FROM 
    (
     SELECT DISTINCT Column3 
     FROM YourTable 
    ) t1 
) d3 ON(n = d3.rn) 
LEFT JOIN 
(
    SELECT Column4, ROW_NUMBER() OVER (ORDER BY Column4) rn 
    FROM 
    (
     SELECT DISTINCT Column4 
     FROM YourTable 
    ) t1 
) d4 ON(n = d4.rn) 
LEFT JOIN 
(
    SELECT Column5, ROW_NUMBER() OVER (ORDER BY Column5) rn 
    FROM 
    (
     SELECT DISTINCT Column5 
     FROM YourTable 
    ) t1 
) d5 ON(n = d5.rn) 

Динамическая версия:

DECLARE @TableName sysname = 'YourTableName' 

DECLARE @Sql nvarchar(max) = 
' 
DECLARE @Count int 
select @Count = COUNT(1) 
FROM '+ @TableName +' 

;with tally as (
    select 1 as n 
    union all 
    select n + 1 
    from tally 
    where n < @Count 
) 

SELECT ' 

SELECT @Sql = @Sql + Column_Name +',' 
FROM INFORMATION_SCHEMA.COLUMNS 
WHERE TABLE_NAME = @TableName 

SELECT @Sql = LEFT(@Sql, LEN(@Sql) - 1) + ' FROM tally t' 

SELECT @Sql = @Sql + ' LEFT JOIN (SELECT '+ Column_Name +', ROW_NUMBER() OVER (ORDER BY ' + Column_Name +') rn 
    FROM 
    (
    SELECT DISTINCT '+ Column_Name +' FROM '+ @TableName +') t 
) c_'+ Column_Name + ' ON(n = c_'+ Column_Name + '.rn)' 
FROM INFORMATION_SCHEMA.COLUMNS 
WHERE TABLE_NAME = @TableName 

EXEC(@Sql) 

Update

Испытано на столе с 22 колоннами и 47000 строк, мое предложение принял 46 секунд при использовании proper tally table. на Sql сервере 2014 Я был удивлен - я думал, что это займет не менее 2-3 минут.

+0

Ты почти там. Теперь сделайте это динамически. :-P –

+0

Включение этого в динамический sql не так уж сложно, но я сомневаюсь, что это будет лучше, чем динамическое решение, предложенное Кана в другом ответе. –

+0

@JeroenMostert: вызов за исключением. –

1

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

Но, кроме этого, это должно работать отлично, а скрипт содержит необходимую информацию, показывающую, как конкатенировать окончательное «WHERE S1.COLNAME NOT NULL, а S2.COLNAME NOT NULL AND ..» фильтрует таблицу результатов, чтобы устранить эти полные нулевые строки.

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

Попробуйте это на меньшем столе и посмотрите, как оно работает.

/* 
Set your table and schema on @MYTABLE and @MYSCHEMA variables. 
*/ 
SET NOCOUNT ON 

DECLARE @MYTABLE SYSNAME = 'Mytablename here' 
    , @MYSCHEMA sysname = 'dbo' 

DECLARE @SQL NVARCHAR(MAX) = '', @COLNAME sysname = '', @MYCOLS NVARCHAR(max) = '' 

DECLARE @COL_NOW INT = 1, @COL_MAX INT = 
    (SELECT COUNT(*) 
    FROM sys.columns 
    WHERE object_id = (SELECT object_id FROM sys.tables where name = @MYTABLE and SCHEMA_NAME(schema_id) = @MYSCHEMA)) 

SELECT @COLNAME = name 
    FROM sys.columns 
    WHERE column_id = 1 
    and object_id = (SELECT object_id FROM sys.tables where name = @MYTABLE and SCHEMA_NAME(schema_id) = @MYSCHEMA) 

SET @SQL = 'FROM 
    (SELECT ROW_NUMBER() OVER (ORDER BY '[email protected]+' ASC) RN 
    FROM '[email protected]+'.'[email protected]+' (nolock)) S' 

WHILE @COL_NOW <= @COL_MAX 
BEGIN 

    SELECT @COLNAME = name 
    FROM sys.columns 
    WHERE column_id = @COL_NOW 
    and object_id = (SELECT object_id FROM sys.tables where name = @MYTABLE and SCHEMA_NAME(schema_id) = @MYSCHEMA) 

    SELECT @SQL = @SQL+' 
FULL JOIN 
    (SELECT DISTINCT DENSE_RANK() OVER (ORDER BY '[email protected]+' ASC) RN, '[email protected]+' 
    FROM '[email protected]+'.'[email protected]+' (nolock) 
    WHERE '[email protected]+' IS NOT NULL) S'+CAST(@COL_NOW AS NVARCHAR(25))+' ON S'+CAST(@COL_NOW AS NVARCHAR(25))+'.RN = S.RN' 

    IF @COL_NOW = 1 
     SELECT @MYCOLS = @MYCOLS+' S'+CAST(@COL_NOW AS NVARCHAR(25))+'.'[email protected]   
    ELSE 
     SELECT @MYCOLS = @MYCOLS+', S'+CAST(@COL_NOW AS NVARCHAR(25))+'.'[email protected] 

    SET @COL_NOW = @COL_NOW+1 

END 

SELECT @SQL = 'SELECT'[email protected]+' 
'[email protected]+' 
ORDER BY S1.RN ASC'; 

--PRINT(@SQL); -- To check resulting dynamic SQL without executing it (Warning, print will only show first 8k characters) 

EXEC sp_executesql @SQL; 
GO 
Смежные вопросы