【问题标题】:Append an ID to fields on a SQL table joined multiple times?将 ID 附加到多次连接的 SQL 表上的字段?
【发布时间】:2020-12-03 03:51:24
【问题描述】:

我有两张桌子

PersonId | Device1 | Device2 | Device3
---------+---------+---------+--------
    1         10       20         30

设备

DeviceId | Name | Description | PicklingCoefficient
---------+------+-------------+--------------------
   10       AA     Whatever             1
   20       BB     Who knows           -1
   30       CC     Blah blah           100

还有一个将第一个到第二个连接三次的查询:

select p.PersonId, d1.*, d2.*, d3.* from People p     
  left join Device d1 on d1.DeviceId = p.Device1
  left join Device d2 on d2.DeviceId = p.Device2
  left join Device d3 on d3.DeviceId = p.Device3

这会生成格式为(人员 + 设备 + 设备 + 设备)的表头,其中 Device 表字段仅重复 3 次:

PersonId | Device1 | Device2 | Device3 | DeviceId | Name | Description | PicklingCoefficient | DeviceId | Name | Description | PicklingCoefficient | DeviceId | Name | Description | PicklingCoefficient

我可以在技术上构建一个字符串生成器,在我提交查询之前为每个 d(n) 表生成一些唯一名称,但是有没有办法简化它?

可能通过在查询的第一行附加一些内容来给我格式(人+设备1+设备2+设备3):

PersonId | Device1 | Device2 | Device3 | DeviceId1 | Name1 | Description1 | PicklingCoefficient1 | DeviceId2 | Name2 | Description2 | PicklingCoefficient2 | DeviceId3 | Name3 | Description3 | PicklingCoefficient3

我希望这样的事情会起作用:

select p.PersonId, d1.* + '1', d2.* + '2', d3.* + '3' from People p 

【问题讨论】:

  • 您可以使用别名,将 d1.* 替换为 d1.DeviceId 为 DeviceId1,d1.Name 替换为 Name1 ...
  • 这就是我想避免的,必须手动输入所有列名,因为 a) 有很多列 b) 连接数是动态的
  • 上述声明不是动态的,@jamheadart。如果是,您应该向我们展示相同的动态语句,因为您很可能能够动态地实现您想要的。但问题是,为什么你的陈述是动态的?
  • 此外,您的People 表包含Device1Device2 等列本身就是一个设计缺陷。也许真正的解决方案是规范你的设计。
  • 为什么每个设备不一行(使用 UNION)?那么你就没有重复的列名了。

标签: sql sql-server


【解决方案1】:

这里真正的问题是你的设计,它是非规范化的。你在这里似乎有一个多对多的关系,这意味着你真正需要做的是规范你的设计。

您应该有另一个表来处理多对多关系,而不是让 people 为一个设备提供 3 个不同的列。简单来说,您的表格应该是这样的:

CREATE TABLE dbo.People (PersonID int IDENTITY PRIMARY KEY,
                         [Name] nvarchar(50)); --Overly simplified
GO
CREATE TABLE dbo.Device (DeviceID int IDENTITY PRIMARY KEY,
                         [Name] char(2),
                         [Description] varchar (50),
                         PicklingCoefficient smallint);
GO
CREATE TABLE dbo.PersonDevice (PersonID int,
                               DeviceID int);

ALTER TABLE dbo.PersonDevice ADD CONSTARINT PersonDevice_PK PRIMARY KEY(PersonID, DeviceID);

ALTER TABLE dbo.PersonDevice ADD CONSTRAINT PersonDevice_PersonFK FOREIGN KEY (PersonID) REFERENCES dbo.People (PersonID);
ALTER TABLE dbo.PersonDevice ADD CONSTRAINT PersonDevice_DeviceFK FOREIGN KEY (DeviceID) REFERENCES dbo.Device (DeviceID);
GO

然后你会像这样INSERT 数据:

INSERT INTO dbo.People ([Name])
VALUES(N'Bob'),(N'Jayne');

INSERT INTO dbo.Device ([Name],[Description],PicklingCoefficient)
VALUES('AA','Whatever',1),
      ('BB','Who knows',-1),
      ('CC','Blah blah',100);
GO
--Then the device relationship
INSERT INTO dbo.PersonDevice(PersonID,DeviceID)
VALUES(1,1),(1,2),(1,3),(2,3);

那么你就不需要多个连接,因为你只有 2 个,并且没有重复的列:

SELECT *
FROM dbo.People P
     JOIN dbo.PersonDevice PD ON P.PersonID = PD.PersonID
     JOIN dbo.Device D ON PD.DeviceID = D.DeviceID;

【讨论】:

  • 数据原来是这样设置的。已经创建了一个视图,该视图可以透视数据并动态附加单个指示符列,但需要 Device 表的许多实例。然后我的工作就是使用这些指标加入该视图,并将每个指标替换为表格的完整实例。
  • 那么你向我们展示的不是这里的真实图片,@jamheadart。您在问题开始时声明“我有两个表”,然后显示非规范化数据。显然,因此,您没有有 2 个表,也没有对它们进行去噪 (?)。向我们展示真正的问题...
  • 我知道,我不可能。我实际上应该忽略多个连接,只是询问是否可以将字符串附加到选择行中的所有表字段。我试图提供一些背景信息,但现在我得到了一个我没有问的问题的答案!
【解决方案2】:

我建议您取消透视并将数据放在单独的行中:

select p.PersonId, v.device_number, d.*
from People p  cross apply
     (values (1, p.Device1), (2, p.Device2), (3, p.Device3)
     ) v(device_number, device) left join
     Device d
     on d.DeviceId = v.device_number
order by p.PersonId, v.device_number;

【讨论】:

  • 它一开始是不旋转的,视图会旋转它,我正在加入视图。我的手有些束缚。我只是要在前端构建一个单独命名每一列的字符串。
  • @jamheadart 。 . .这种观点可能对你没有任何好处。
  • 我需要很长时间才能在这里将其分解并最小化,我会在一天结束时看看我能做些什么。
猜你喜欢
  • 2018-12-07
  • 2020-04-21
  • 2013-02-20
  • 2013-10-26
  • 1970-01-01
  • 2018-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多