我正在使用Sql Server 2012作为我的DBMS。
在我的数据库中,我有一个与一系列目录表相关的Product表。这些目录表代表各种产品类别。我们的想法是,虽然所有产品都有一些共同的属性(我们的内部标识符,供应商,成本等),但产品的具体情况会有所不同(家具的描述方式与数字产品的描述方式不同)
此外,每个产品都有一个父实体。父母可能有多个孩子(或在这种情况下是产品)。
我的任务是为给定的父ID列表选择产品和相关信息,并将该信息填充到XML文档中。
我目前正在使用的表是:
产品与Product_Id上的PRODUCT_DIGITAL有PK / FK关系。 PRODUCT到PRODUCT_FURNITURE存在相同类型的关系。
如果产品看起来像这样:
PRODUCT_ID -- PRODUCT_CATEGORY -- PARENT_ID -- PARENT_TYPE -- DELIVERY_IN_DAYS
100 DIG 1 1 7
101 DIG 1 1 8
102 DIG 1 1 1
103 DIG 2 1 2
104 DIG 2 1 1
PRODUCT_DIGITAL看起来像这样:
PRODUCT_ID -- PRODUCT_TYPE -- PRODUCT_NAME -- PRODUCT_MNEMONIC
100 A IMG1 IMAWTRFL
101 B SND1 SNDENGRV
102 B SND2 SNDHRSLF
103 A IMG2 IMGNBRTO
104 B SND3 SNDGTWNE
最后我希望结果集如下所示:
PRODUCT_CATEGORY -- PRODUCT_ID -- PRODUCT_TYPE -- PARENT_ID -- DELIVERY_IN_DAYS -- PROD_EXTENSION_NAME -- PROD_EXTENSION_TYPE -- PROD_EXTENSION_VALUE
DIG 100 A 1 7 PRODUCT_NAME STRING IMG1
DIG 100 A 1 7 PRODUCT_MNEMONIC STRING IMAWTRFL
DIG 101 B 1 8 PRODUCT_NAME STRING SND1
DIG 101 B 1 8 PRODUCT_MNEMONIC STRING SNDENGRV
DIG 102 B 1 1 PRODUCT_NAME STRING SND2
DIG 102 B 1 1 PRODUCT_MNEMONIC STRING SNDHRSLF
DIG 103 A 2 2 PRODUCT_NAME STRING IMG2
DIG 103 A 2 2 PRODUCT_MNEMONIC STRING IMGNBRTO
DIG 104 B 2 1 PRODUCT_NAME STRING SND3
DIG 104 B 2 1 PRODUCT_MNEMONIC STRING SNDGTWNE
我原来的搜索把我带到了UNPIVOT - 但到目前为止我还没有能够理解它。我最终做的是创建一个临时表并在传递中更新它,然后加入到产品表中进行最终选择:
create table #tbl(product_id int, prod_extension_name varchar(100), prod_extension_type varchar(100), prod_extension_value varchar(1000))
insert into #tbl
select p.product_id, c.column_name,
case c.data_type
when 'varchar' then 'string'
else data_type end as data_type
, null
from dbo.product p, information_schema.columns c
where c.table_name = 'PRODUCT_DIGITAL'
and c.column_name in ('PRODUCT_NAME','PRODUCT_MNEMONIC')
update #tbl
set prod_extension_value = p.product_name
from dbo.product p
where #tbl.product_id = p.product_id
and #tbl.colname = 'PRODUCT_NAME'
update #tbl
set prod_extension_value = p.product_mnemonic
from dbo.product p
where #tbl.product_id = p.product_id
and #tbl.colname = 'PRODUCT_MNEMONIC'
select p.product_category, p.product_id, pd.product_category, #tbl.prod_extension_name, #tbl.prod_extension_type, #tbl.prod_extension_value
from dbo.product p inner join dbo.product_digital pd on p.product_id = pd.product_id
inner join #tbl on p.product_id = #tbl.product_id
order by product_id
有人能指出我更好的方法吗?看起来我应该能够更快地做到这一点,而不必进行多次更新等等。
答案 0 :(得分:4)
我认为这会做你想要的。这实现了UNPIVOT
函数,然后从information_schema.columns
视图中获取列信息以获得结果:
select product_id,
product_category,
parent_id,
delivery_in_days,
PROD_EXTENSION_NAME,
case when c.data_type = 'varchar'
then 'STRING' else null end as PROD_EXTENSION_TYPE,
PROD_EXTENSION_VALUE
from
(
select
p.product_id,
p.product_category,
p.parent_id,
p.delivery_in_days,
PRODUCT_NAME,
PRODUCT_MNEMONIC
from product p
left join product_digital pd
on p.product_id = pd.product_id
) src
unpivot
(
PROD_EXTENSION_VALUE
for PROD_EXTENSION_NAME in (PRODUCT_NAME, PRODUCT_MNEMONIC)
) up
inner join
(
select c.column_name, c.data_type
from information_schema.columns c
where c.table_name = 'PRODUCT_DIGITAL'
and c.column_name in ('PRODUCT_NAME','PRODUCT_MNEMONIC')
) c
on up.PROD_EXTENSION_NAME = c.column_name
结果是:
| PRODUCT_ID | PRODUCT_CATEGORY | PARENT_ID | DELIVERY_IN_DAYS | PROD_EXTENSION_NAME | PROD_EXTENSION_TYPE | PROD_EXTENSION_VALUE |
-----------------------------------------------------------------------------------------------------------------------------------
| 100 | DIG | 1 | 7 | PRODUCT_NAME | STRING | IMG1 |
| 100 | DIG | 1 | 7 | PRODUCT_MNEMONIC | STRING | IMAWTRFL |
| 101 | DIG | 1 | 8 | PRODUCT_NAME | STRING | SND1 |
| 101 | DIG | 1 | 8 | PRODUCT_MNEMONIC | STRING | SNDENGRV |
| 102 | DIG | 1 | 1 | PRODUCT_NAME | STRING | SND2 |
| 102 | DIG | 1 | 1 | PRODUCT_MNEMONIC | STRING | SNDHRSLF |
| 103 | DIG | 2 | 2 | PRODUCT_NAME | STRING | IMG2 |
| 103 | DIG | 2 | 2 | PRODUCT_MNEMONIC | STRING | IMGNBRTO |
| 104 | DIG | 2 | 1 | PRODUCT_NAME | STRING | SND3 |
| 104 | DIG | 2 | 1 | PRODUCT_MNEMONIC | STRING | SNDGTWNE |
编辑#1:如果要动态执行此类转换,则需要使用动态SQL。为此,您将使用以下内容。
首先,您需要将列列表添加到UNPIVOT
:
select @colsUnpivot = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('PRODUCT_DIGITAL') and
C.name not in ('PRODUCT_ID', 'PRODUCT_TYPE') -- include the items you DO NOT want to unpivot
for xml path('')), 1, 1, '')
最终的剧本将是:
DECLARE @colsUnpivot AS NVARCHAR(MAX),
@cols 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('PRODUCT_DIGITAL') and
C.name not in ('PRODUCT_ID', 'PRODUCT_TYPE')
for xml path('')), 1, 1, '')
select @cols = stuff((select ', '''+C.name + ''''
from sys.columns as C
where C.object_id = object_id('PRODUCT_DIGITAL') and
C.name not in ('PRODUCT_ID', 'PRODUCT_TYPE')
for xml path('')), 1, 1, '')
set @query
= ' select
product_id,
product_category,
parent_id,
delivery_in_days,
PROD_EXTENSION_NAME,
case when c.data_type = ''varchar''
then ''STRING'' else null end as PROD_EXTENSION_TYPE,
PROD_EXTENSION_VALUE
from
(
select
p.product_id,
p.product_category,
p.parent_id,
p.delivery_in_days,
PRODUCT_NAME,
PRODUCT_MNEMONIC
from product p
left join product_digital pd
on p.product_id = pd.product_id
) src
unpivot
(
PROD_EXTENSION_VALUE
for PROD_EXTENSION_NAME in ('+ @colsunpivot +')
) up
inner join
(
select c.column_name, c.data_type
from information_schema.columns c
where c.table_name = ''PRODUCT_DIGITAL''
and c.column_name in ('+@cols+')
) c
on up.PROD_EXTENSION_NAME = c.column_name'
exec(@query)
见SQL Fiddle with Demo。除了动态之外,这将产生与原始版本相同的结果。
答案 1 :(得分:0)
我可能会开枪,但你说你的最终输出应该是XML。 虽然我没有包括您的所有要求,但这样的事情是否符合您的要求? (我想我可能已经过度简化了你的要求)
;WITH PRODUCT (PRODUCT_ID, PRODUCT_CATEGORY, PARENT_ID, PARENT_TYPE, DELIVERY_IN_DAYS) AS
(
SELECT 100, 'DIG', 1, 1, 7 UNION ALL
SELECT 101, 'DIG', 1, 1, 8 UNION ALL
SELECT 102, 'DIG', 1, 1, 1 UNION ALL
SELECT 103, 'DIG', 2, 1, 2 UNION ALL
SELECT 104, 'DIG', 2, 1, 1
)
,PRODUCT_DIGITAL (PRODUCT_ID, PRODUCT_TYPE, PRODUCT_NAME, PRODUCT_MNEMONIC) AS
(
SELECT 100, 'A', 'IMG1', 'IMAWTRFL' UNION ALL
SELECT 101, 'B', 'SND1', 'SNDENGRV' UNION ALL
SELECT 102, 'B', 'SND2', 'SNDHRSLF' UNION ALL
SELECT 103, 'A', 'IMG2', 'IMGNBRTO' UNION ALL
SELECT 104, 'B', 'SND3', 'SNDGTWNE'
)
SELECT P.PRODUCT_CATEGORY
,P.PRODUCT_ID
,P.PARENT_TYPE
,P.PARENT_ID
,P.DELIVERY_IN_DAYS
,PD.PRODUCT_NAME
,PD.PRODUCT_MNEMONIC
FROM PRODUCT P
JOIN PRODUCT_DIGITAL PD ON P.PRODUCT_ID = PD.PRODUCT_ID
FOR XML PATH