sql从表到表插入

时间:2014-08-11 12:34:06

标签: sql sql-server sql-server-2008

我有一个包含这些列的表Farm

FarmID:(primary)
Kenizy:
BarBedo:
BarBodo:
MorKodo:

这些列是某种语言的手掌类型。其中的每一列都包含一个数字,表示农场内此类棕榈的数量。

示例:

FarmID  | Kenizy  | BarBedo | BarBodo | MorKodo
-----------------------------------------------
3       |   20    |   12    |   45    |   60
22      |   21    |    9    |    41   |   3

我想将该表插入下表:

Palm_Farm

FarmID:(primary)
PalmID;(primary)
PalmTypeName:
Count:

该表将每个农场与每种手掌类型连接起来。

示例:

FarmID   |   PalmID   |  PalmTypeName   |  Count
-----------------------------------------------
   3     |     1      |      Kenizy     |   20
   3     |     2      |    BarBedo      |  12
   3     |     3      |     BarBodo     |  45
   3     |     4      |     MorKodo     |  60
  22     |     1      |     Kenizy      |  21
  22     |     2      |     BarBedo     |  9
  22     |     3      |     BarBodo    |   41
  22     |     4      |     MorKodo    |   3

我必须使用下表Palms才能获取PalmID列。

PalmID:(primary)
PlamTypeName:
...other not important columns

此表用于保存有关每种手掌类型的信息。

示例:

PalmID  |    PlamTypeName 
-------------------------
   1    |  Kenizy     
   2    |  BarBedo     
   3    |  BarBodo       
   4    |  MorKodo    

PalmTypeName列的值与Farm表中的 COLUMN NAMES 相同。

所以我的问题是:

考虑到Farm表中存在PalmID,如何将Palm_Farm表中的数据插入Palm

我希望我可以明白我的问题,我自己尝试解决问题,但Farm表中的列名必须是column value in Palm_Farm表无法知道如何操作。

我无法更改表结构,因为我们正在尝试使用此已存在的表来帮助客户

我正在使用SQL Server 2008,因此欢迎使用Merge

更新

天才回答@GarethD之后,我得到了这个例外

enter image description here

1 个答案:

答案 0 :(得分:2)

您可以使用UNPIVOT将列转换为行:

INSERT Palm_Farm (FarmID, PalmID, PalmTypeName, [Count])
SELECT  upvt.FarmID,
        p.PalmID,
        p.PalmTypeName,
        upvt.[Count]
FROM    Farm AS f
        UNPIVOT
        (   [Count]
            FOR PalmTypeName IN ([Kenizy], [BarBedo], [BarBodo], [MorKodo])
        ) AS upvt
        INNER JOIN Palms AS p
            ON p.PalmTypeName = upvt.PalmTypeName;

<强> Example on SQL Fiddle

UNPIVOT州的文档:

  

UNPIVOT通过将列旋转到行中来执行几乎PIVOT的反向操作。假设前一个示例中生成的表作为pvt存储在数据库中,并且您希望将列标识符Emp1,Emp2,Emp3,Emp4和Emp5旋转为与特定供应商对应的行值。这意味着您必须识别另外两列。包含要旋转的列值的列(Emp1,Emp2,...)将被称为Employee,并且将保存当前位于要旋转的列下的值的列将被称为Orders。这些列分别对应于Transact-SQL定义中的pivot_column和value_column。

为了进一步解释unpivot如何工作,我将看第一行原始表:

FarmID  | Kenizy  | BarBedo | BarBodo | MorKodo
-----------------------------------------------
3       |   20    |   12    |   45    |   60

所以UPIVOT会做的是查找UNPIVOT语句中指定的列,并为每列创建一行:

SELECT  upvt.FarmID, upvt.PalmTypeName, upvt.[Count]
FROM    Farm AS f
        UNPIVOT
        (   [Count]
            FOR PalmTypeName IN ([Kenizy], [BarBedo])
        ) AS upvt;

所以在这里你要说的是,对于每一行都找到列[Kenizy][BarBedo]并为每个行创建一行,然后为每个行创建一个名为PalmTypeName的新列,将包含使用的列名,然后将该列的值放入名为[Count]的新列中。给出结果:

FarmID  | Kenizy  | Count |
---------------------------
3       | Kenizy  |  20   | 
3       | BarBedo |  12   |

如果您运行的是SQL Server 2000或兼容级别较低的更高版本,则可能需要使用其他查询:

INSERT Palm_Farm (FarmID, PalmID, PalmTypeName, [Count])
SELECT  f.FarmID,
        p.PalmID,
        p.PalmTypeName,
        [Count] = CASE upvt.PalmTypeName
                    WHEN 'Kenizy' THEN f.Kenizy
                    WHEN 'BarBedo' THEN f.BarBedo
                    WHEN 'BarBodo' THEN f.BarBodo
                    WHEN 'MorKodo' THEN f.MorKodo
                END
FROM    Farm AS f
        CROSS JOIN
        (   SELECT  PalmTypeName = 'Kenizy' UNION ALL
            SELECT  PalmTypeName = 'BarBedo' UNION ALL
            SELECT  PalmTypeName = 'BarBodo' UNION ALL
            SELECT  PalmTypeName = 'MorKodo'
        ) AS upvt
        INNER JOIN Palms AS p
            ON p.PalmTypeName = upvt.PalmTypeName;

这类似,但您必须使用子查询UNION ALL内的upvt自行创建其他行,然后使用案例表达式选择[Count]的值。


要在行存在时更新,您可以使用MERGE

WITH Data AS
(   SELECT  upvt.FarmID,
            p.PalmID,
            p.PalmTypeName,
            upvt.[Count]
    FROM    Farm AS f
            UNPIVOT
            (   [Count]
                FOR PalmTypeName IN ([Kenizy], [BarBedo], [BarBodo], [MorKodo])
            ) AS upvt
            INNER JOIN Palms AS p
                ON p.PalmTypeName = upvt.PalmTypeName
)
MERGE Palm_Farm WITH (HOLDLOCK) AS pf
USING Data AS d
    ON d.FarmID = pf.FarmID
    AND d.PalmID = pf.PalmID
WHEN NOT MATCHED BY TARGET THEN 
    INSERT (FarmID, PalmID, PalmTypeName, [Count])
    VALUES (d.FarmID, d.PalmID, d.PalmTypeName, d.[Count])
WHEN MATCHED THEN 
    UPDATE
    SET [Count] = d.[Count],
        PalmTypeName = d.PalmTypeName;