基于列名的sql更新
Posted
技术标签:
【中文标题】基于列名的sql更新【英文标题】:sql update based on column names 【发布时间】:2013-02-13 11:01:10 【问题描述】:我有问题。
我有T1
、T2
、T_Join
表。
T_Join
:第一列:ID
(唯一)例如:10,11,12,13。第二列:CODE
,它包含的属性等于T2
的列名。例如:类型、来源、部分、重要性。这些由T1
中的ID
标识。据此,属性'source'的ID是11。
ID CODE
10 type
11 source
12 section
13 importance
在表T1
中,第一列是data_ID
,它不是唯一的:1020、1020、1020、1022、1022、1022、1023、1023、1028、1028、1028、1035、1035等。
第二列是来自T_Join
的ID。在这个例子中,4个ID可以属于1个data_ID
,这些声明,其中的值出现在第三列(VALUE):
data_ID ID VALUE
1020 10 1
1020 11 123
1020 12 9
1020 13 4
1022 10 2
1022 12 15
1023 10 2
1023 11 108
1023 13 2
1028 12 20
...
表示ID为1020的物品为类型1,来源于No.123,该ID标识的真实对象存储在第9节中,重要性为4级。
现在,我有一张桌子 T2。第一列与 T1 中的 data_ID 相同。在此表中,这些是唯一的。其他列:(多么令人惊讶!)类型、来源、部分、重要性。 (实际上,不仅有四个属性,而且至少有五十个!) 所以表格看起来像这样:
data_ID type source section importance
1020 1 123 9 2
1022 1 95 3 5
1023 2 108 21 4
1028 1 147 17 5
T2 包含较新的数据。我想用这些更新 T1.VALUE 列。按照我上面的例子,更新后的 T1 应该是这样的:
data_ID ID VALUE
1020 10 1
1020 11 123
1020 12 9
1020 13 2
1022 10 1
1022 12 3
1023 10 2
1023 11 108
1023 13 4
1028 12 17
...
因此,在 data_ID 1020 处,重要性为 4,然后变为 2,因为在 T1 中,ID 为 13,它指的是 T_Join 表中的属性“重要性”,依此类推。 我想以这种方式更新所有数据。我不是 SQL 专家,但我设法创建了这段代码:
update T1 set VALUE =
(select * from T2
inner join T_Join on ID=
(SELECT
c.name
FROM
sys.objects o
INNER JOIN
sys.columns c
ON
c.object_id = o.object_id
AND o.name = 'T2')
where T1.data_ID = T2.data_ID and T2.ID = T_Join.ID)
from T1
inner join T2 on T1.data_ID = T2.data_ID
inner join T_Join on T1.ID = T_Join.ID
select * from T1
但它不起作用,错误信息:
消息 116,第 16 级,状态 1,第 16 行 当子查询不使用 EXISTS 引入时,选择列表中只能指定一个表达式。
我尝试使用 CURSOR 语句和声明变量(基于建议)来解决它,但它也不起作用。
如果有人知道我该如何解决这个问题(以最简单的方式),请尽可能详细地回答。
【问题讨论】:
+1 您提供了很多有关您遇到的问题的详细信息,甚至还展示了您尝试解决的问题。这是一篇很好的第一篇文章。 【参考方案1】:您当前设计的问题是您有一个已规范化的表和一个非规范化的表,您需要执行更新。
首先,您需要对 T2
表进行反规范化,该表将获取列并将其转换为行。在 SQL Server 2005+ 中,他们引入了 UNPIVOT
函数,它将为您执行此操作。
第一步是将SELECT
来自T2
和T_Join
的数据放入行。 SELECT
语句是:
select j.id,
j.code,
u.data_id,
u.value
from T_Join j
inner join
(
select data_id, col, value
from T2
unpivot
(
value
for col in (type, source, section, importance)
) unpiv
) u
on j.code = u.col
见SQL Fiddle with Demo。这将获取您的列数据并将其转换为给出结果的行:
| ID | CODE | DATA_ID | VALUE |
-------------------------------------
| 10 | type | 1020 | 1 |
| 11 | source | 1020 | 123 |
| 12 | section | 1020 | 9 |
| 13 | importance | 1020 | 2 |
| 10 | type | 1022 | 1 |
| 11 | source | 1022 | 95 |
| 12 | section | 1022 | 3 |
| 13 | importance | 1022 | 5 |
| 10 | type | 1023 | 2 |
| 11 | source | 1023 | 108 |
| 12 | section | 1023 | 21 |
| 13 | importance | 1023 | 4 |
| 10 | type | 1028 | 1 |
| 11 | source | 1028 | 147 |
| 12 | section | 1028 | 17 |
| 13 | importance | 1028 | 5 |
一旦数据采用该格式,您就可以在UPDATE
语句中使用它:
update t1
set t1.value = t.value
from t1
inner join
(
select j.id,
j.code,
u.data_id,
u.value
from T_Join j
inner join
(
select data_id, col, value
from T2
unpivot
(
value
for col in (type, source, section, importance)
) unpiv
) u
on j.code = u.col
) t
on t1.data_id = t.data_id
and t1.id = t.id;
见SQL Fiddle with Demo。
您所说的下一个问题是您需要unpivot 大约 50 列。如果是这种情况,那么您可以使用动态 SQL 来获取要转换为行的列列表。您的动态 SQL 脚本将是:
DECLARE @colsUnpivot AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @colsUnpivot = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('T2') and
C.name not in ('data_ID')
for xml path('')), 1, 1, '')
set @query
= 'update t1
set t1.value = t.value
from t1
inner join
(
select j.id,
j.code,
u.data_id,
u.value
from T_Join j
inner join
(
select data_id, col, value
from T2
unpivot
(
value
for col in ('+@colsUnpivot+')
) unpiv
) u
on j.code = u.col
) t
on t1.data_id = t.data_id
and t1.id = t.id;'
exec(@query);
见SQL Fiddle with Demo。
代码将更新T1
,结果如下:
| DATA_ID | ID | VALUE |
------------------------
| 1020 | 10 | 1 |
| 1020 | 11 | 123 |
| 1020 | 12 | 9 |
| 1020 | 13 | 2 |
| 1022 | 10 | 1 |
| 1022 | 12 | 3 |
| 1023 | 10 | 2 |
| 1023 | 11 | 108 |
| 1023 | 13 | 4 |
| 1028 | 12 | 17 |
【讨论】:
感谢您的回答,我会尽快查看!以上是关于基于列名的sql更新的主要内容,如果未能解决你的问题,请参考以下文章
SQL篇章SQL语句梳理 :--基于MySQL5.6已梳理:ALTER TABLE解析