【问题标题】:Cartesian product between two tables when the second one doesn't have all records当第二个表没有所有记录时,两个表之间的笛卡尔积
【发布时间】:2021-12-03 05:48:14
【问题描述】:

我在 SQL Server 中有 2 个表,我想通过它们的 ID 获取所有可能的组合,即使第二个没有记录,我想知道是否有比我更好的解决方案。

我的两张桌子是:

  • 拥有 10 条记录的公司(CompanyId、Tot1、Tot2)
  • 具有 4 条记录的电机(MotorId、CompanyId_FK、TotSales1、TotSales2)

,这里是创建和填充它们的脚本:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Companies](
    [CompanyId] [int] IDENTITY(1,1) NOT NULL,
    [Tot1] [int] NOT NULL,
    [Tot2] [int] NOT NULL,
 CONSTRAINT [PK_Companies] PRIMARY KEY CLUSTERED 
(
    [CompanyId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object:  Table [dbo].[Motors]    Script Date: 10/15/2021 9:18:31 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Motors](
    [MotorId] [int] IDENTITY(1,1) NOT NULL,
    [CompanyId] [int] NOT NULL,
    [TotSales1] [int] NOT NULL,
    [TotSales2] [int] NOT NULL,
 CONSTRAINT [PK_Motors] PRIMARY KEY CLUSTERED 
(
    [MotorId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[Companies] ON 
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (1, 300, 200)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (2, 400, 100)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (3, 500, 100)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (4, 600, 200)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (5, 700, 500)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (6, 800, 400)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (7, 900, 300)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (8, 50, 20)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (9, 80, 20)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (10, 40, 10)
GO
SET IDENTITY_INSERT [dbo].[Companies] OFF
GO
SET IDENTITY_INSERT [dbo].[Motors] ON 
GO
INSERT [dbo].[Motors] ([MotorId], [CompanyId], [TotSales1], [TotSales2]) VALUES (1, 4, 35, 23)
GO
INSERT [dbo].[Motors] ([MotorId], [CompanyId], [TotSales1], [TotSales2]) VALUES (2, 5, 140, 70)
GO
INSERT [dbo].[Motors] ([MotorId], [CompanyId], [TotSales1], [TotSales2]) VALUES (3, 7, 200, 24)
GO
INSERT [dbo].[Motors] ([MotorId], [CompanyId], [TotSales1], [TotSales2]) VALUES (4, 9, 2, 1)
GO
SET IDENTITY_INSERT [dbo].[Motors] OFF
GO
ALTER TABLE [dbo].[Motors]  WITH CHECK ADD  CONSTRAINT [FK_Motors_Companies] FOREIGN KEY([CompanyId])
REFERENCES [dbo].[Companies] ([CompanyId])
GO
ALTER TABLE [dbo].[Motors] CHECK CONSTRAINT [FK_Motors_Companies]
GO

我需要一个包含 Motor 和 Company 的所有可能组合的列表作为结果集,即使没有销售:

CompanyId MotorId Tot1 Tot2 TotSales1 TotSales2
4 1 600 200 35 23
4 2 600 200 NULL NULL
4 3 600 200 NULL NULL
4 4 600 200 NULL NULL

我通过 CROSS JOIN 创建了一个只有 CompanyId 和 MotorId 的 CTE,然后我以这种方式加入了另外两个:

;WITH myTable AS (
SELECT Companies.CompanyId, MotorId
FROM Companies CROSS JOIN Motors
GROUP BY Companies.CompanyId, MotorId)
 
SELECT myTable.*, Tot1, Tot2, TotSales1,TotSales2 FROM myTable
INNER JOIN Companies ON myTable.CompanyId = Companies.CompanyId
LEFT OUTER JOIN Motors ON myTable.CompanyId = Motors.CompanyId AND myTable.MotorId = Motors.MotorId

有没有更好的解决方案?为避免误解:我无法更改字段的顺序以将结果映射到 C# 类。

【问题讨论】:

  • 提问时,您需要提供minimal reproducible example: (1) DDL 和样本数据填充,即 CREATE 表和 INSERT T-SQL 语句。 (2) 你需要做什么,即逻辑和你的代码尝试在 T-SQL 中实现它。 (3) 期望的输出,基于上述#1 中的样本数据。 (4) 您的 SQL Server 版本 (SELECT @@version;)。
  • 理想情况下,您应该有一个完全独立的Motor 表,而这个Motors 表应该真正称为CompanyMotor,然后您只需交叉加入前两个,然后左加入最后一个。
  • @YitzhakKhabinsky 我的帖子现在正确吗?
  • @Charlieface 是的,就像我对 CTE 所做的那样,对吧?
  • 理想情况下它应该是一个真实的物理表。您当前的设计有些缺陷

标签: sql sql-server tsql cartesian-product sql-server-2019


【解决方案1】:

您已经非常接近答案了...只是不要在您的 CTE 中执行 GROUP BY,您将得到您想要的结果:

;WITH myTable AS (
SELECT Companies.CompanyId, MotorId
FROM Companies CROSS JOIN Motors
) 
SELECT myTable.*, Tot1, Tot2, TotSales1,TotSales2 FROM myTable
INNER JOIN Companies ON myTable.CompanyId = Companies.CompanyId
LEFT OUTER JOIN Motors ON myTable.CompanyId = Motors.CompanyId AND myTable.MotorId = Motors.MotorId

我不知道你为什么认为你需要GROUP BY,但因为你想要所有可能的组合,所以不需要它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-03
    • 2013-11-16
    • 2013-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-24
    相关资源
    最近更新 更多