SQL沿列移动数据

时间:2016-02-10 16:35:32

标签: sql sql-server sql-server-2012

我不知道从哪里开始,但这是我需要做的......

我们有一张地址和电话号码表。但是我需要将6个电话号码列从6个减少到3个。将数字从右向左移动到任何空单元格中。

以下示例 -

表格如何 What the table looks like

我希望它看起来像什么 What i want it to look like

3 个答案:

答案 0 :(得分:4)

PIVOTUNPIVOT将能够完成这项工作。想法:

  1. UNPIVOT将数据分成行
  2. 清空空行并计算新列的内容
  3. PIVOT将已清理的数据恢复为列。
  4. 这是在一个声明中使用一堆CTE来实现它的一种方法。注意我假设有一个ID列,我已经组成了表名:

    ;WITH Unpivoted AS
    (
        -- our data into rows
        SELECT ID, TelField, Tel
        FROM Telephone
        UNPIVOT
        (
            Tel FOR TelField IN (TEL01,TEL02,TEL03,TEL04,TEL05,TEL06)
        ) as up
    ),
    Cleaned AS
    (
        -- cleaning the empty rows
        SELECT
            'TEL0' + CAST(ROW_NUMBER() OVER (PARTITION BY ID ORDER BY TelField) AS VARCHAR) [NewTelField],
            ID,
            TelField,
            Tel
        FROM Unpivoted
        WHERE NULLIF(NULLIF(Tel, ''), 'n/a') IS NOT NULL
    ),
    Pivoted AS
    (
        -- pivoting back into columns
        SELECT ID, TEL01, TEL02, TEL03
        FROM
        (
            SELECT ID, NewTelField, Tel
            FROM Cleaned
        ) t
        PIVOT
        (
            -- simply add ", TEL04, TEL05, TEL06" if you want to still see the
            -- other columns (or if you will have more than 3 final telephone numbers)
            MIN(Tel) FOR NewTelField IN (TEL01, TEL02, TEL03)
        ) pvt
    )
    SELECT * FROM Pivoted
    ORDER BY ID
    

    这会将电话号码一次性转移到正确的位置。您还可以将Pivoted中的SELECT * FROM Pivoted更改为任何其他CTE - UnpivotedCleaned - 以查看部分结果的外观。最终结果:

    Results

答案 1 :(得分:0)

有几种不同的方法可以做到这一点,最好的选择取决于您使用的数据库引擎,和/或其他可用的工具。

最脏的方法可能是编写64个不同的sql语句来获取64种可能组合中的每一种的数据(包括6个字段都是空白的,并且6个字段都是填充的)但是,你可能不会在实践中使用它。

所以问题是找到一个简单,可重复的算法来将问题空间缩小到更简单的东西。

执行此操作的一种方法是编写6个语句,每个语句在可能的情况下执行一个移位,位于一个空格的左侧。

e.g。

update table set field1 = field2, field2 = null from table
where field2 is not null and field1 is null

当且仅当field1为空,且字段2不为空时,将内容从field2移动到字段1

第二个声明:

update table set field2 = field3, field3 = null from table
where field3 is not null and field2 is null

是否相同 - 依此类推:

update table set field3 = field4, field4 = null from table
where field4 is not null and field3 is null

update table set field4 = field5, field5 = null from table
where field5 is not null and field4 is null

update table set field5 = field6, field6 = null from table
where field6 is not null and field2 is null

这样总共5个语句 - 将它们全部作为批处理运行将逐步将记录移动至少每批一个 - 如果左边有空的空间,它将每批移动更多并且右边的所有记录都是相邻的,没有间隙。差距往往会向右移动,因此我猜测通过运行批次至少6次,您应该发现自己的数据集符合您的要求。

你需要的实际sql将取决于你的RDBMS语言如何处理空值或空字符串或其他什么,但好消息是你只需要5个语句,你只需要运行它们的次数相对较少。

可能有比这更聪明的方法,但这很简单,每次批量运行都会让你离你想要的位置更近一步。在不利方面,它有点笨重。如果你需要定期运行,我可能想要找出更优雅的东西。

根据数据大小,可能值得运行一些分类/计算每个组合/空计数的记录数的东西。这样就可以减少你必须做的次数 - 可能只有4或5种不同的模式,而不是完整的64种。

答案 2 :(得分:0)

示例数据:

IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp; 

SELECT * 
INTO #temp
FROM 
(SELECT NULL TEL1, NULL TEL2, 1 TEL3, NULL TEL4, 2 TEL5,3 TEL6
UNION ALL
SELECT 4 TEL1, NULL TEL2,5 TEL3, NULL TEL4, NULL TEL5,NULL TEL6
UNION ALL
SELECT 6 TEL1, NULL TEL2,7 TEL3, NULL TEL4, NULL TEL5,NULL TEL6
UNION ALL
SELECT NULL TEL1, 8 TEL2,9 TEL3, 10 TEL4, NULL TEL5,NULL TEL6
UNION ALL
SELECT NULL TEL1, NULL TEL2,11 TEL3, NULL TEL4, 12 TEL5,NULL TEL6
UNION ALL
SELECT NULL TEL1, 13 TEL2,14 TEL3, NULL TEL4, 15 TEL5,NULL TEL6
UNION ALL
SELECT 16 TEL1, NULL TEL2,17 TEL3, NULL TEL4, NULL TEL5,18 TEL6
UNION ALL
SELECT NULL TEL1, 19 TEL2,20 TEL3, NULL TEL4, NULL TEL5,21 TEL6
UNION ALL
SELECT 22 TEL1, NULL TEL2,23 TEL3, NULL TEL4, 24 TEL5,NULL TEL6
UNION ALL
SELECT NULL TEL1, 25 TEL2,26 TEL3, NULL TEL4, NULL TEL5,27 TEL6) AS A 



SELECT * FROM #temp

enter image description here

解决方案:

;WITH CTE AS (
SELECT ID, 'TEL' + CAST(ROW_NUMBER() OVER (PARTITION BY ID ORDER BY PHONE) AS VARCHAR) AS PHONE,VALUE FROM 
(SELECT NEWID() AS ID,* FROM #temp) AS P UNPIVOT
(Value FOR Phone IN (TEL1   ,TEL2,  TEL3,   TEL4,   TEL5,   TEL6 )) AS unpvt)

SELECT [TEL1],[TEL2],[TEL3] FROM
(SELECT * FROM CTE) P PIVOT (MAX(VALUE) FOR Phone IN
( [TEL1],[TEL2],[TEL3] )
) AS pvt

结果: enter image description here