4

我正在为我的 DBMS 使用 Sql Server 2012。

在我的数据库中,我有一个与一系列目录表相关的产品表。这些目录表代表各种产品类别。这个想法是,虽然所有产品都有某些共同的属性(我们的内部标识符、供应商、成本等),但产品的细节会有所不同(家具的描述方式与描述数字产品的方式不同) .

此外,每个产品都有一个父实体。父母可能有多个孩子(或在这种情况下的产品)。

我的任务是为给定的父 ID 列表选择产品和相关信息,并将该信息填充到 XML 文档中。

我目前正在使用的表是:

  • 产品
  • 产品_数码
  • 产品中心_家具

Product 与 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

有人可以指出一个更好的方法来做到这一点吗?似乎我应该能够更快地做到这一点,而不必进行多次更新等。

4

2 回答 2

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           

请参阅带有演示的 SQL Fiddle

结果是:

| 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。这将产生与原始版本相同的结果,但它是动态的。

于 2013-01-09T23:58:28.723 回答
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
于 2013-01-10T12:00:14.397 回答