在SQL Server 2000中将一组行转置为列

8

在SQL Server中是否有将行转置为列的功能(在MS-Access中可以实现)?我感到困惑,因为在MS-Access中有此功能,但在SQL Server中却没有。这个特性被故意在SQL Server中排除了吗?

5个回答

3
这个例子只有在你事先知道行值时才能起作用。例如,假设你有一个带有自定义属性的实体,并且这些自定义属性被实现为子表中的行,其中子表基本上是变量/值对,并且这些变量/值对是可配置的。
color red
size  big
city  Chicago

我将描述一种有效的技术,我已经使用过它。我不是在推销它,但它确实有效。
为了透视数据,其中您事先不知道值是什么,请创建一个没有列的临时表。然后使用游标循环遍历行,为每个变量发出动态构建的“alter table”,以便最终您的临时表具有列,颜色,大小和城市。
然后,在您的临时表中插入一行,通过另一个游标通过变量值对进行更新,然后选择它,通常与其父实体连接,实际上使其看起来像那些自定义变量/值对就像原始父实体中的内置列一样。

2
所描述的游标方法可能是最不像SQL的使用方法。正如提到的,SQL 2005及以上版本具有很好的PIVOT功能。但对于旧版本和非MS SQL服务器,来自“Optimzing Transact-SQL”(编辑:已绝版,但可从亚马逊购买:http://www.amazon.com/Optimizing-Transact-SQL-Advanced-Programming-Techniques/dp/0964981203)的Rozenshtein方法非常适合数据透视和反透视。 它使用点特征将基于行的数据转换为列。 Rozenshtein描述了几种情况,这里是一个例子:
SELECT
    RowValueNowAColumn = 
       CONVERT(varchar,
           MAX(
          SUBSTRING(myTable.MyVarCharColumn,1,DATALENGTH(myTable.MyVarCharColumn)
       * CHARINDEX(sa.SearchAttributeName,'MyRowValue'))))
FROM
    myTable

这种方法比使用case语句更高效,适用于各种数据类型和SQL实现(不仅限于MS SQL)。


1
最好在这种情况下限制小规模。如果您正在使用SQL 2k,但没有可用的PIVOT功能,我已经起草了一个存储过程,应该可以为您完成工作。有点仓促,所以尽管随意修改。将以下内容粘贴到sql窗口中,并根据需要编辑底部的EXEC。如果您想查看正在生成的内容,请删除中间的--s:
IF EXISTS (SELECT * FROM SYSOBJECTS WHERE XTYPE = 'P' AND NAME = 'USP_LIST_CONCAT')
DROP PROCEDURE USP_LIST_CONCAT
GO

CREATE PROCEDURE USP_LIST_CONCAT (@SourceTable NVARCHAR(1000) = '' ,@SplitColumn NVARCHAR(1000) = '' , @Deli NVARCHAR(10) = '', @KeyColumns NVARCHAR(2000) = '' , @Condition NVARCHAR(1000) = '')
AS
BEGIN
SET NOCOUNT ON

/* PROCEDURE CREATED 2010 FOR SQL SERVER 2000. SIMON HUGHES. */
/* NOTES: REMOVE --'s BELOW TO LIST GENERATED SQL. */

IF @SourceTable = '' OR @SourceTable = '?' OR @SourceTable = '/?' OR @SplitColumn = '' OR @KeyColumns = ''
BEGIN
PRINT 'Format for use:'
PRINT ' USP_LIST_CONCAT ''SourceTable'', ''SplitColumn'', ''Deli'', ''KeyColumn1,...'', ''Column1 = 12345 AND ...'''
PRINT ''
PRINT 'Description:'
PRINT 'The SourceTable should contain a number of records acting as a list of values.'
PRINT 'The SplitColumn should be the name of the column holding the values wanted.'
PRINT 'The Delimiter may be any single character or string ie ''/'''
PRINT 'The KeyColumn may contain a comma separated list of columns that will be returned before the concatenated list.'
PRINT 'The optional Conditions may be left blank or may include the following as examples:'
PRINT ' ''Column1 = 12334 AND (Column2 = ''ABC'' OR Column3 = ''DEF'')'''
PRINT ''
PRINT 'A standard list in the format:'
PRINT ' Store1, Employee1, Rabbits'
PRINT ' Store1, Employee1, Dogs'
PRINT ' Store1, Employee1, Cats'
PRINT ' Store1, Employee2, Dogs'
PRINT ''
PRINT 'Will be returned as:'
PRINT ' Store1, Employee1, Cats/Dogs/Rabbits'
PRINT ' Store1, Employee2, Dogs'
PRINT ''
PRINT 'A full ORDER BY and DISTINCT is included'
RETURN -1
END


DECLARE @SQLStatement NVARCHAR(4000)

SELECT @SQLStatement = '
DECLARE @DynamicSQLStatement NVARCHAR(4000)

SELECT @DynamicSQLStatement = ''SELECT '+@KeyColumns+', SUBSTRING(''

SELECT @DynamicSQLStatement = @DynamicSQLStatement + '' + '' + CHAR(10) +
'' MAX(CASE WHEN '+@SplitColumn+' = ''''''+RTRIM('+@SplitColumn+')+'''''' THEN '''''+@Deli+'''+RTRIM('+@SplitColumn+')+'''''' ELSE '''''''' END)''
FROM '+ @SourceTable +' ORDER BY '+@SplitColumn+'

SELECT @DynamicSQLStatement = @DynamicSQLStatement + '' ,2,7999) List'' + CHAR(10) + ''FROM '+ @SourceTable+''' + CHAR(10) +'''+CASE WHEN @Condition = '' THEN '/* WHERE */' ELSE 'WHERE '+@Condition END+ '''+ CHAR(10) + ''GROUP BY '+@KeyColumns+'''

SELECT @DynamicSQLStatement = REPLACE(@DynamicSQLStatement,''( +'',''('')

-- SELECT @DynamicSQLStatement -- DEBUG ONLY
EXEC (@DynamicSQLStatement)'

EXEC (@SQLStatement)

END
GO

EXEC USP_LIST_CONCAT 'MyTableName', 'ColumnForListing', 'Delimiter', 'KeyCol1, KeyCol2', 'Column1 = 123456'

0

0

我有以下格式的数据

调查问题ID

电子邮件(用户)

答案

对于1个调查,有13个问题和答案,我想要的期望输出是

用户 ---调查问题ID1---调查问题ID2

电子邮件---答案---答案........等等

这里是SQL Server 2000的解决方案,因为字段数据类型是TEXT

DROP TABLE #tmp

DECLARE @tmpTable  TABLE
(
emailno NUMERIC,
question1 VARCHAR(80),
question2 VARCHAR(80),
question3 VARCHAR(80),
question4 VARCHAR(80),
question5 VARCHAR(80),
question6 VARCHAR(80),
question7 VARCHAR(80),
question8 VARCHAR(80),
question9 VARCHAR(80),
question10 VARCHAR(80),
question11 VARCHAR(80),
question12 VARCHAR(80),
question13 VARCHAR(8000)
)

DECLARE @tmpTable2  TABLE
(
emailNumber NUMERIC
)

DECLARE @counter INT
DECLARE @Email INT

SELECT @counter =COUNT(DISTINCT ans.email) FROM answers ans WHERE ans.surveyname=100430 AND ans.qnpkey BETWEEN 233702 AND 233714
SELECT * INTO #tmp FROM @tmpTable
INSERT INTO @tmpTable2 (emailNumber) SELECT DISTINCT CAST(ans.email AS NUMERIC) FROM answers ans WHERE ans.surveyname=100430 AND ans.qnpkey BETWEEN 233702 AND 233714

WHILE @counter >0

BEGIN

        SELECT TOP 1 @Email= emailNumber FROM @tmpTable2
        INSERT INTO @tmpTable (emailno) VALUES (@Email )


        Update @tmpTable set question1=CAST(answer as VARCHAR(80)) from answers ans where ans.surveyname=100430 and ans.qnpkey = 233702 and ans.email=@Email
        Update @tmpTable set question2=CAST(answer as VARCHAR(80)) from answers ans where ans.surveyname=100430 and ans.qnpkey = 233703 and email=@email
        Update @tmpTable set question3=CAST(answer as VARCHAR(80)) from answers ans where ans.surveyname=100430 and ans.qnpkey = 233704 and email=@email
        Update @tmpTable set question4=CAST(answer as VARCHAR(80)) from answers ans where ans.surveyname=100430 and ans.qnpkey = 233705 and email=@email
        Update @tmpTable set question5=CAST(answer as VARCHAR(80)) from answers ans where ans.surveyname=100430 and ans.qnpkey = 233706 and email=@email
        Update @tmpTable set question6=CAST(answer as VARCHAR(80)) from answers ans where ans.surveyname=100430 and ans.qnpkey = 233707 and email=@email
        Update @tmpTable set question7=CAST(answer as VARCHAR(80)) from answers ans where ans.surveyname=100430 and ans.qnpkey = 233708 and email=@email
        Update @tmpTable set question8=CAST(answer as VARCHAR(80)) from answers ans where ans.surveyname=100430 and ans.qnpkey = 233709 and email=@email
        Update @tmpTable set question9=CAST(answer as VARCHAR(80)) from answers ans where ans.surveyname=100430 and ans.qnpkey = 233710 and email=@email
        Update @tmpTable set question10=CAST(answer as VARCHAR(80)) from answers ans where ans.surveyname=100430 and ans.qnpkey = 233711 and email=@email
        Update @tmpTable set question11=CAST(answer as VARCHAR(80)) from answers ans where ans.surveyname=100430 and ans.qnpkey = 233712 and email=@email
        Update @tmpTable set question12=CAST(answer as VARCHAR(80)) from answers ans where ans.surveyname=100430 and ans.qnpkey = 233713 and email=@email
        Update @tmpTable set question13=CAST(answer as VARCHAR(8000)) from answers ans where ans.surveyname=100430 and ans.qnpkey = 233714 and email=@email

        insert into #tmp select * from  @tmpTable       

        DELETE FROM @tmpTable       
        DELETE FROM @tmpTable2 WHERE emailNumber= @Email

        set @counter =@counter -1

End

select * from #tmp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接