【问题标题】:merging and transposing tables合并和转置表
【发布时间】:2022-11-03 00:44:44
【问题描述】:

我的样本数据集如下,

| Customer |    |Detail |   |DataValues |
|----------|    |-------|   |-----------|
| ID       |    |ID     |   |CustomerID |
| Name     |    |Name   |   |DetailID   |
                            |Values |

| Customer |    |Detail   |     |DataValues |
|----------|    |---------|     |-----------|
| 1 | Jack |    | 1 | sex |     | 1 | 1 | M |
| 2 | Anne |    | 2 | age |     | 1 | 2 | 30|
                                | 2 | 1 | F |
                                | 2 | 2 | 28|

我想要的结果如下,

Name Sex Age
Jack M 30
Anne F 28

我没有想出一个正确的返回任何东西的 SQL 查询。

提前致谢。

select Customers.Name, Details.Name, DataValues.Value from Customers
inner join DataValues on DataValues.CustomersID = Customers.ID 
inner join Details on DataValues.DetailsID = Details.ID

    

【问题讨论】:

标签: sql-server join merge transpose


【解决方案1】:

静态方式,假设你知道你想要SexAge

WITH cte AS 
(
  SELECT c.Name, Type = d.Name, dv.[Values]
  FROM dbo.DataValues AS dv
  INNER JOIN dbo.Detail AS d
  ON dv.DetailID = d.ID
  INNER JOIN dbo.Customer AS c
  ON dv.CustomerID = c.ID
  WHERE d.Name IN (N'Sex',N'Age')
)
SELECT Name, Sex, Age
  FROM cte
  PIVOT (MAX([Values]) FOR [Type] IN ([Sex],[Age])) AS p;

如果您需要基于所有可能的属性派生查询,那么您将需要使用动态 SQL。这是一种方法:

DECLARE @in  nvarchar(max),
        @piv nvarchar(max),
        @sql nvarchar(max);

SELECT @in  = STRING_AGG(N'N' + QUOTENAME(Name, char(39)), ','),
       @piv = STRING_AGG(QUOTENAME(Name), ',')
  FROM (SELECT Name FROM dbo.Detail GROUP BY Name) AS src;

SET @sql = N'WITH cte AS 
(
  SELECT c.Name, Type = d.Name, dv.[Values]
  FROM dbo.DataValues AS dv
  INNER JOIN dbo.Detail AS d
  ON dv.DetailID = d.ID
  INNER JOIN dbo.Customer AS c
  ON dv.CustomerID = c.ID
  WHERE d.Name IN (' + @in + N')
)
SELECT Name, ' + @piv + N'
  FROM cte
  PIVOT (MAX([Values]) FOR [Type] IN (' + @piv + N')) AS p;';

EXECUTE sys.sp_executesql @sql;

工作示例in this fiddle

【讨论】:

    【解决方案2】:

    这里有很多东西要解开。让我们从如何呈现演示数据开始:

    如果您为您的数据提供 DDL 和 DML,那么人们可以更轻松地使用:

    DECLARE @Customer TABLE (ID INT, Name NVARCHAR(100))
    DECLARE @Detail TABLE (ID INT, Name NVARCHAR(20))
    DECLARE @DataValues TABLE (CustomerID INT, DetailID INT, [Values] NVARCHAR(20))
    
    INSERT INTO @Customer (ID, Name) VALUES
    (1, 'Jack'),(2, 'Anne')
    INSERT INTO @Detail (ID, Name) VALUES
    (1, 'Sex'),(2, 'Age')
    INSERT INTO @DataValues (CustomerID, DetailID, [Values]) VALUES
    (1, 1, 'M'),(1, 2, '30'),(2, 1, 'F'),(2, 2, '28')
    

    这将设置您的表(作为变量)并使用演示数据填充它们。

    接下来让我们谈谈这里的可怕架构。 您也应该始终尽量避免使用保留字作为列名。 Values 是一个关键字。 这可能应该是一个客户表:

    DECLARE @Genders TABLE (ID INT IDENTITY, Name NVARCHAR(20))
    DECLARE @Customer1 TABLE (CustomerID INT IDENTITY, Name NVARCHAR(100), BirthDate DATETIME, GenderID INT NULL, Age AS (DATEDIFF(YEAR, BirthDate, CURRENT_TIMESTAMP)))
    

    请注意,我使用的是 BirthDate 而不是 Age。这是因为一个人的年龄会随着时间而改变,但他们的出生日期不会。不应存储基于另一个属性计算的属性(但如果您愿意,可以使用计算列,就像我们在这里一样)。您还会注意到,我们将通过 Gender ID 引用它,而不是在客户表中明确定义性别。这是一个查找表。

    如果您使用了规范化模式,您的查询将如下所示:

    /* Demo Data */
    DECLARE @Genders TABLE (ID INT IDENTITY, Name NVARCHAR(20));
    INSERT INTO @Genders (Name) VALUES
    ('Male'),('Female'),('Non-Binary');
    DECLARE @Customer1 TABLE (CustomerID INT IDENTITY, Name NVARCHAR(100), BirthDate DATETIME, GenderID INT NULL, Age AS (DATEDIFF(YEAR, BirthDate, CURRENT_TIMESTAMP)));
    INSERT INTO @Customer1 (Name, BirthDate, GenderID) VALUES
    ('Jack', '2000-11-03', 1),('Anne', '2000-11-01', 2),('Chris', '2001-05-13', NULL);
    
    /* Query */
    SELECT *
      FROM @Customer1 c
        LEFT OUTER JOIN @Genders g
          ON c.GenderID = g.ID;
    

    现在介绍如何从您拥有的结构中获取您想要的数据。无论如何,您这样做将是杂技,因为我们必须针对模式工作。

    /* Demo Data */
    DECLARE @Customer TABLE (ID INT, Name NVARCHAR(100));
    DECLARE @Detail TABLE (ID INT, Name NVARCHAR(20));
    DECLARE @DataValues TABLE (CustomerID INT, DetailID INT, [Values] NVARCHAR(20));
    
    INSERT INTO @Customer (ID, Name) VALUES
    (1, 'Jack'),(2, 'Anne');
    INSERT INTO @Detail (ID, Name) VALUES
    (1, 'Sex'),(2, 'Age');
    INSERT INTO @DataValues (CustomerID, DetailID, [Values]) VALUES
    (1, 1, 'M'),(1, 2, '30'),(2, 1, 'F'),(2, 2, '28');
    
    /* Query */
    SELECT *
      FROM (
            SELECT d.Name AS DetailName, c.Name AS CustomerName, DV.[Values]
             FROM @DataValues dv
               INNER JOIN @Detail d
                 ON dv.DetailID = d.ID
               INNER JOIN @Customer c
                 ON dv.CustomerID = c.ID
           ) a
        PIVOT (
               MAX([Values]) FOR DetailName IN (Sex,Age)
              ) p;
    
    CustomerName    Sex Age
    -----------------------
    Anne            F   28
    Jack            M   30
    

    【讨论】:

      猜你喜欢
      • 2013-02-03
      • 1970-01-01
      • 2021-10-07
      • 2021-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-18
      相关资源
      最近更新 更多