逐行或逐列

时间:2017-12-13 22:02:16

标签: sql-server

我有一个涉及数据结构的2D数组的应用程序,它被保存到表CellItem(一个元素一个记录)

CREATE TABLE CellItem ( 
    ID INT, 
    IdRow INT -- FK RowName, 
    IdCol INT -- FK ColName, 
    Value varchar(max))

为了列出行和列元素,我定义了2个其他表RowItem和ColItem

CREATE TABLE RowItem ( ID INT, IdRow INT, IDCellIem INT)
CREATE TABLE ColItem ( ID INT, IdCol INT, IDCellIem INT)

其中IDCellIem是CellItem表中引用ID的外键 和IdRow和IdCol分别是在下面定义的RowName和ColName中引用ID的外键

CREATE TABLE RowName ( ID INT, name varchar(max))
CREATE TABLE ColName ( ID INT, name varchar(max))

我无法想出一个简洁的解决方案。我接近完成了一些将旋转,动态SQL和STUFF语句嵌套到游标循环中的东西。 但我发现它很复杂。

假设一个3x2数组我期望某种SELECT返回3个记录集,其中2列用于行元素,或2个记录集用3列列出col元素 例如

我希望能够按列列出结果(不仅是按记录集的一个元素)

col name1 ; elt1,1 ; elt2,1
col name2 ; elt1,2 ; elt2,2
col name3 ; elt1,3 ; elt2,3

或带有结果的行(不仅是记录集中的一个元素)

row name1  ; elt1,1 ; elt1,2 ; elt1,3
row name2  ; elt2,1 ; elt2,2 ; elt2,3

我的问题是如何编写存储过程的脚本以列出适合记录集的整个行或列元素,如上所述。很容易获得单行或col,但更复杂的是获得所有这些。 格式化输出对于我有1000行x 200列的客户端来说是性能至关重要的

接近强制查询类似于@ Leran2002提供的(下面),其中@VariableColumns是我想要选择的列的列表,用STUFF()语句获取(因为我说我的代码太复杂了)。 #Temp表有我可以从中提取值的内容

SET @Query = 
'
SELECT
    ' + @VariableColumns + '
FROM 
(
    SELECT
        [rowID] AS pivotVariable, 
        [value]
    FROM #Temp                  
) tmp
PIVOT 
(
    max([value])
    FOR pivotVariable in (' +  @VariableColumns + ')
) piv'

EXEC(@Query);

2 个答案:

答案 0 :(得分:1)

这类问题经常在这里问。例如 - Display count results of requests with results of jobs horizontally and locations vertically 3 tables

但我也为你的案子制作了剧本。希望我理解这个问题。如果我错了请评论。如果你给我们一些测试数据,它会很好。

我想问一个问题。 为什么不使用CellItem而不使用RowItemColItem的以下结构?

CREATE TABLE CellItem(
  ID INT,
  IdRow INT, -- FK to RowName
  IdCol INT, -- FK to ColName
  Value varchar(max)
)

我的回答。

测试数据

CREATE TABLE CellItem ( ID INT, Value varchar(max))

CREATE TABLE RowItem ( ID INT, IdRow INT, IDCellIem INT)
CREATE TABLE ColItem ( ID INT, IdCol INT, IDCellIem INT)

CREATE TABLE RowName ( ID INT, name varchar(max))
CREATE TABLE ColName ( ID INT, name varchar(max))


INSERT RowName(ID,name)VALUES
(1,'r1'),
(2,'r2'),
(9,'r9')

INSERT ColName(ID,name)VALUES
(1,'c1'),
(2,'c2'),
(3,'c3'),
(9,'c9')

INSERT CellItem(ID,value)VALUES
(11,'v11'),
(12,'v12'),
(22,'v22'),
(23,'v23')

INSERT RowItem(ID,IdRow,IDCellIem)VALUES
(1,1,11),
(2,1,12),
(3,2,22),
(4,2,23)

INSERT ColItem(ID,IdCol,IDCellIem)VALUES
(1,1,11),
(2,2,12),
(3,2,22),
(4,3,23)

如果您希望仅显示已填充的行和列

DECLARE
  @colIndexes varchar(MAX)='',
  @colNames varchar(MAX)=''

SELECT
  @colIndexes+=CONCAT(',',QUOTENAME(ID)),
  @colNames+=CONCAT(',',QUOTENAME(ID),' ',QUOTENAME(name))
FROM ColName
WHERE ID IN(SELECT IdCol FROM ColItem) -- only filled columns

SET @colIndexes=STUFF(@colIndexes,1,1,'')

PRINT @colIndexes
PRINT @colNames

DECLARE @query varchar(MAX)='SELECT IdRow,NameRow'+@colNames+'
FROM
  (
    SELECT
      r.IdRow,rn.name NameRow,
      c.IdCol,
      i.Value
    FROM CellItem i
    JOIN ColItem c ON i.ID=c.IDCellIem
    JOIN RowItem r ON i.ID=r.IDCellIem JOIN RowName rn ON rn.ID=r.IdRow
  ) q PIVOT(MAX(Value) FOR IdCol IN('+@colIndexes+')) p
'

PRINT @query

EXEC(@query)
GO

列为行

DECLARE
  @rowIndexes varchar(MAX)='',
  @rowNames varchar(MAX)=''

SELECT
  @rowIndexes+=CONCAT(',',QUOTENAME(ID)),
  @rowNames+=CONCAT(',',QUOTENAME(ID),' ',QUOTENAME(name))
FROM RowName
WHERE ID IN(SELECT IdRow FROM RowItem) -- only filled rows

SET @rowIndexes=STUFF(@rowIndexes,1,1,'')

PRINT @rowIndexes
PRINT @rowNames

DECLARE @query varchar(MAX)='SELECT IdCol,NameCol'+@rowNames+'
FROM
  (
    SELECT
      r.IdRow,
      c.IdCol,cn.name NameCol,
      i.Value
    FROM CellItem i
    JOIN RowItem r ON i.ID=r.IDCellIem
    JOIN ColItem c ON i.ID=c.IDCellIem JOIN ColName cn ON cn.ID=c.IdCol
  ) q PIVOT(MAX(Value) FOR IdRow IN('+@rowIndexes+')) p
'

PRINT @query

EXEC(@query)
GO

如果要显示所有行和列

DECLARE
  @colIndexes varchar(MAX)='',
  @colNames varchar(MAX)=''

SELECT
  @colIndexes+=CONCAT(',',QUOTENAME(ID)),
  @colNames+=CONCAT(',',QUOTENAME(ID),' ',QUOTENAME(name))
FROM ColName

SET @colIndexes=STUFF(@colIndexes,1,1,'')

PRINT @colIndexes
PRINT @colNames

DECLARE @query varchar(MAX)='SELECT n.ID IdRow,n.name NameRow'+@colNames+'
FROM
  (
    SELECT
      r.IdRow,
      c.IdCol,
      i.Value
    FROM CellItem i
    JOIN ColItem c ON i.ID=c.IDCellIem
    JOIN RowItem r ON i.ID=r.IDCellIem
  ) q PIVOT(MAX(Value) FOR IdCol IN('+@colIndexes+')) p
RIGHT JOIN RowName n ON n.ID=p.IdRow
'

PRINT @query

EXEC(@query)
GO

列为行

DECLARE
  @rowIndexes varchar(MAX)='',
  @rowNames varchar(MAX)=''

SELECT
  @rowIndexes+=CONCAT(',',QUOTENAME(ID)),
  @rowNames+=CONCAT(',',QUOTENAME(ID),' ',QUOTENAME(name))
FROM RowName

SET @rowIndexes=STUFF(@rowIndexes,1,1,'')

PRINT @rowIndexes
PRINT @rowNames

DECLARE @query varchar(MAX)='SELECT n.ID IdCol,n.name NameCol'+@rowNames+'
FROM
  (
    SELECT
      r.IdRow,
      c.IdCol,
      i.Value
    FROM CellItem i
    JOIN RowItem r ON i.ID=r.IDCellIem
    JOIN ColItem c ON i.ID=c.IDCellIem
  ) q PIVOT(MAX(Value) FOR IdRow IN('+@rowIndexes+')) p
RIGHT JOIN ColName n ON n.ID=p.IdCol
'

PRINT @query

EXEC(@query)
GO

答案 1 :(得分:1)

如果单元格可以包含多个值,并且您希望使用逗号连接这些值,则可以尝试以下操作。看我的评论。您可以运行所有脚本,因为我在此示例中使用了临时表。

-- test tables and values
CREATE TABLE #CellItem(
  ID INT,
  IdRow INT, -- FK to RowName
  IdCol INT, -- FK to ColName
  Value varchar(max)
)

CREATE TABLE #RowName ( ID INT, name varchar(max))
CREATE TABLE #ColName ( ID INT, name varchar(max))

INSERT #RowName(ID,name)VALUES
(1,'r1'),
(2,'r2'),
(9,'r9')

INSERT #ColName(ID,name)VALUES
(1,'c1'),
(2,'c2'),
(3,'c3'),
(9,'c9')

INSERT #CellItem(ID,IdRow,IdCol,Value)VALUES
(1,1,1,'v11-a'), (2,1,1,'v11-b'), -- cell(1,1) contains 2 values
(3,1,2,'v12-a'),
(4,1,3,'v13-a'),
(5,2,1,'v21-a'), (6,2,1,'v21-b'), (7,2,1,'v21-с') -- cell(2,1) contains 3 values

-- variant 1 - rows by vertical
DECLARE
  @colIndexes varchar(MAX)='',
  @colNames varchar(MAX)=''

SELECT
  @colIndexes+=CONCAT(',',QUOTENAME(ID)),
  @colNames+=CONCAT(',',QUOTENAME(ID),' ',QUOTENAME(name))
FROM ColName
--WHERE ID IN(SELECT DISTINCT IdCol FROM #CellItem) -- if you want to hide empty columns

SET @colIndexes=STUFF(@colIndexes,1,1,'')

PRINT @colIndexes
PRINT @colNames

DECLARE @query1 varchar(MAX)='SELECT n.ID IdRow,n.name NameRow'+@colNames+'
FROM
  (
    SELECT
      k.IdRow,
      k.IdCol,
      STUFF(
          (
          SELECT ''; ''+i.Value
          FROM #CellItem i
          WHERE i.IdCol=k.IdCol AND i.IdRow=k.IdRow
          ORDER BY i.ID
          FOR XML PATH('''')
          ),1,2,'''') Value
    FROM
      (
        SELECT DISTINCT IdRow,IdCol
        FROM #CellItem
      ) k
  ) q PIVOT(MAX(Value) FOR IdCol IN('+@colIndexes+')) p
JOIN #RowName n ON n.ID=p.IdRow -- use RIGHT JOIN if you want show empty rows
'

PRINT @query1
EXEC(@query1)

-- variant 2 - rows by horisontal
DECLARE
  @rowIndexes varchar(MAX)='',
  @rowNames varchar(MAX)=''

SELECT
  @rowIndexes+=CONCAT(',',QUOTENAME(ID)),
  @rowNames+=CONCAT(',',QUOTENAME(ID),' ',QUOTENAME(name))
FROM RowName
--WHERE ID IN(SELECT DISTINCT IdCol FROM #CellItem) -- if you want to hide empty rows

SET @rowIndexes=STUFF(@rowIndexes,1,1,'')

PRINT @rowIndexes
PRINT @rowNames

DECLARE @query2 varchar(MAX)='SELECT n.ID IdCol,n.name NameCol'+@rowNames+'
FROM
  (
    SELECT
      k.IdRow,
      k.IdCol,
      STUFF(
          (
          SELECT ''; ''+i.Value
          FROM #CellItem i
          WHERE i.IdCol=k.IdCol AND i.IdRow=k.IdRow
          ORDER BY i.ID
          FOR XML PATH('''')
          ),1,2,'''') Value
    FROM
      (
        SELECT DISTINCT IdRow,IdCol
        FROM #CellItem
      ) k
  ) q PIVOT(MAX(Value) FOR IdRow IN('+@rowIndexes+')) p
JOIN #ColName n ON n.ID=p.IdCol -- use RIGHT JOIN if you want show empty columns
'

PRINT @query2
EXEC(@query2)

DROP TABLE #CellItem
DROP TABLE #RowName
DROP TABLE #ColName
GO

您可以使用其他临时表(请参阅#TempData)代替子查询

-- test tables and values
CREATE TABLE #CellItem(
  ID INT,
  IdRow INT, -- FK to RowName
  IdCol INT, -- FK to ColName
  Value varchar(max)
)

CREATE TABLE #RowName ( ID INT, name varchar(max))
CREATE TABLE #ColName ( ID INT, name varchar(max))

INSERT #RowName(ID,name)VALUES
(1,'r1'),
(2,'r2'),
(9,'r9')

INSERT #ColName(ID,name)VALUES
(1,'c1'),
(2,'c2'),
(3,'c3'),
(9,'c9')

INSERT #CellItem(ID,IdRow,IdCol,Value)VALUES
(1,1,1,'v11-a'), (2,1,1,'v11-b'), -- cell(1,1) contains 2 values
(3,1,2,'v12-a'),
(4,1,3,'v13-a'),
(5,2,1,'v21-a'), (6,2,1,'v21-b'), (7,2,1,'v21-с') -- cell(2,1) contains 3 values


-- an additional temp table
SELECT
  k.IdRow,
  k.IdCol,
  STUFF(
      (
      SELECT '; '+i.Value
      FROM #CellItem i
      WHERE i.IdCol=k.IdCol AND i.IdRow=k.IdRow
      ORDER BY i.ID
      FOR XML PATH('')
      ),1,2,'') Value
INTO #TempData
FROM
  (
    SELECT DISTINCT IdRow,IdCol
    FROM #CellItem
  ) k



-- variant 1 - rows by vertical
DECLARE
  @colIndexes varchar(MAX)='',
  @colNames varchar(MAX)=''

SELECT
  @colIndexes+=CONCAT(',',QUOTENAME(ID)),
  @colNames+=CONCAT(',',QUOTENAME(ID),' ',QUOTENAME(name))
FROM ColName
--WHERE ID IN(SELECT DISTINCT IdCol FROM #CellItem) -- if you want to hide empty columns

SET @colIndexes=STUFF(@colIndexes,1,1,'')

PRINT @colIndexes
PRINT @colNames

DECLARE @query1 varchar(MAX)='SELECT n.ID IdRow,n.name NameRow'+@colNames+'
FROM #TempData q PIVOT(MAX(Value) FOR IdCol IN('+@colIndexes+')) p
JOIN #RowName n ON n.ID=p.IdRow -- use RIGHT JOIN if you want show empty rows
'

PRINT @query1
EXEC(@query1)

-- variant 2 - rows by horisontal
DECLARE
  @rowIndexes varchar(MAX)='',
  @rowNames varchar(MAX)=''

SELECT
  @rowIndexes+=CONCAT(',',QUOTENAME(ID)),
  @rowNames+=CONCAT(',',QUOTENAME(ID),' ',QUOTENAME(name))
FROM RowName
--WHERE ID IN(SELECT DISTINCT IdCol FROM #CellItem) -- if you want to hide empty rows

SET @rowIndexes=STUFF(@rowIndexes,1,1,'')

PRINT @rowIndexes
PRINT @rowNames

DECLARE @query2 varchar(MAX)='SELECT n.ID IdCol,n.name NameCol'+@rowNames+'
FROM #TempData q PIVOT(MAX(Value) FOR IdRow IN('+@rowIndexes+')) p
JOIN #ColName n ON n.ID=p.IdCol -- use RIGHT JOIN if you want show empty columns
'

PRINT @query2
EXEC(@query2)

DROP TABLE #TempData

DROP TABLE #CellItem
DROP TABLE #RowName
DROP TABLE #ColName
GO