【问题标题】:Flatten multiple query results with same ID to single row?将具有相同 ID 的多个查询结果展平为单行?
【发布时间】:2016-07-18 19:40:06
【问题描述】:

我对 SQL Server 数据库中的某些东西很好奇。我当前的查询提取了有关我雇主待售物品的数据。它找到了不到 105,000 个项目的信息,这是正确的。但是,它返回超过 155,000 行,因为每个项目都有其他相关的东西。现在,我通过 Python 中的循环运行该数据,通过检查循环正在处理的项目是否与它刚刚处理的项目相同,手动将其展平。如果是,我开始填写该项目的额外信息。理想情况下,SQL 将返回所有已放入一行的数据。

这里是设置的概述。为简单起见,我省略了一些细节,因为我对一般理论感到好奇,而不是寻找可以复制和粘贴的东西。

商品:包含商品 ID、SKU、描述、供应商 ID、重量和尺寸。

AttributeName:包含 attr_id 和 attr_text。例如,“颜色”、“尺寸”或“样式”。

AttributeValue:包含 attr_value_id 和 attr_text。例如,“蓝色”或“小”。

AttributeAssign:包含 item_id 和 attr_id。这会将属性名称与项目联系起来。

attributeValueAssign:包含 item_id 和 attr_value_id,将属性值绑定到项目。

一系列附件以类似的方式设置,但带有附件和附件分配。附件可以只有值,没有名称,因此不需要像属性那样额外复杂的第三个表。

供应商很简单:在项目表中使用 ID。那就是:

select item_id, vendorName
from item
join vendor on vendor_id = item.vendorNumber

为您获取物品供应商的名称。

现在,有趣的部分是:物品可能有也可能没有供应商、属性或附件。如果他们有后两者中的任何一个,就无法知道他们有多少。我见过属性为 0 的项目和属性为 5 的项目。附件更简单,因为每个项目只能有 0 或 1 个,但 0 的可能性仍然需要外部左连接,所以我保证得到所有项目。

这就是我获得每个项目多行的方式。如果一个项目有三个属性,我会为那个项目得到四行或七行——我不确定它是每个名称/值一行还是每个名称一行和每个值一行。无论哪种方式,这都是我想停止的事情。我希望结果集中的每一行都包含所有属性,上限为 7,任何缺失的属性为 null。也就是说,类似于:

item_id;项目标题; item_sku; ...属性1_name;属性1_值;属性2_名称;属性2_值; ... 属性 7_value
1个;一些随机项目; 123-45; ... 颜色;蓝色;尺寸;中等的; ... 空

现在,我会为此获得多行,例如(仅 ID 和属性):

身份证;属性 1 名称;属性 1 值;属性 2 名称;属性 2 值 1个;颜色;蓝色;空值;空
1个;颜色;蓝色;尺寸;中等

我只关注第二行——所有信息都放在每个唯一项目 ID 的一行中。不过,目前,我得到了多行,而 Python 必须将所有内容放在一起。我将其输出到电子表格,因此有关项目的信息必须在该项目的行上。

如果这太麻烦了,我可以继续使用 Python。但我想知道是否有一种相对容易的方法来做到这一点。我的脚本工作正常,执行时间不是问题。这更多是出于我自己的好奇心,而不是需要让任何东西正常工作。关于如何(或是否)这是可能的任何想法?

【问题讨论】:

  • 一个 MSSQL 数据库,但这不重要”很重要。字符串聚合(这是你想要的)是小菜一碟,例如Postgres,但不在 SQL Server 中。 Edit您的问题并添加一些格式正确的示例数据和预期的输出(请格式化文本

标签: sql sql-server


【解决方案1】:

这是@WCWedin 修改为使用 CTE 的答案。

WITH attrib_rn as
(
  select
    *, row_number() over(partition by item_id order by name, attribute_id) as row_number
  from attributes
)
select
  i.item_id,
  attr1.name as attribute1_name, attr1.value as attribute1_value,
  ...
  attr7.name as attribute7_name, attr7.value as attribute7_value
from items i
left join attrib_rn as attr1 ON attr1.item_id = i.item_id AND attr1.row_number = 1
left join attrib_rn as attr2 ON attr2.item_id = i.item_id AND attr2.row_number = 2
left join attrib_rn as attr3 ON attr3.item_id = i.item_id AND attr3.row_number = 3 
left join attrib_rn as attr4 ON attr4.item_id = i.item_id AND attr4.row_number = 4 
left join attrib_rn as attr5 ON attr5.item_id = i.item_id AND attr5.row_number = 5
left join attrib_rn as attr6 ON attr6.item_id = i.item_id AND attr6.row_number = 6
left join attrib_rn as attr7 ON attr7.item_id = i.item_id AND attr7.row_number = 7

【讨论】:

  • 谢谢,这是有道理的。我注意到在七个连接中,您使用不同的行号,但您总是与 attr1 进行比较。例如。 “左加入 attrib_rn 作为 attr6 ON attr1.item_id = i.item_id AND attr1.row_number = 6”。您是要使用 attr6,还是应该始终与 attr1 进行比较?对不起,如果这是一个愚蠢的问题;我仍在学习 SQL 的细节,并想确保我有这个权利。谢谢。
  • @AH16 不是一个愚蠢的问题——我错了。
  • 再次感谢大家。我只是将此 CTE 放入我的查询中并运行它。一切似乎都正常工作,但查询现在重复的代码更少,这很棒。我还从这次交流中学到了一些非常有用的信息。这太棒了!
【解决方案2】:

由于您只需要前 7 个属性并且希望保留 SQL 查询中的所有逻辑,因此您可能正在考虑使用 row_number。子查询将直接通过多个连接完成这项工作,并且性能可能会非常好,因为您只连接了这么多次。

select
  i.item_id,
  attr1.name as attribute1_name, attr1.value as attribute1_value,
  ...
  attr7.name as attribute7_name, attr7.value as attribute7_value
 from
  items i
  left join (
   select
      *, row_number() over(partition by item_id order by name, attribute_id) as row_number
     from
      attributes
   ) AS attr1 ON
   attr1.item_id = i.item_id
   AND attr1.row_number = 1
  ...
  left join (
   select
      *, row_number() over(partition by item_id order by name, attribute_id) as row_number
     from
      attributes
   ) AS attr7 ON
   attr7.item_id = i.item_id
   AND attr7.row_number = 7

【讨论】:

  • 我从来不知道 row_number() 存在。我想我跟着,但想检查一下。您使用 row_number() 仅获取同一子查询的第 n 行?也就是说,子查询每次都是相同的,除了你只从中得到第 n 行,其中 n 从 1 变为 7?有点像索引到数组?我得试试这个!
  • @WCWedin -- 您应该将属性子查询放在 CTE 中,然后每次都加入同一个表。
  • 再次感谢;这似乎有效。我得到一些重复的值,但我想知道它们是否在数据库中重复,而不是在此查询中重复。我将不得不看看我是否可以使用类似的逻辑来进一步缩小我的结果,因为我仍然从某个地方获得了额外的 32,000 行。超过 51,000 个额外内容和额外的 Python 代码!
  • @Hogan,您能简要解释一下使用 CTE 是如何工作的吗?与 row_number() 一样,我以前从未使用过 CTE,现在仍在阅读它们。我知道您会将“select ... from attribute...”部分放在一个中,但是您将如何在单个查询中创建和调用 CTE,并多次使用它?
  • @AH16 - 看我的回答
【解决方案3】:

在 SQL Server 中,您可以使用包含“ROW_NUMBER() OVER”的子查询和一些 CASE 语句将前 7 个映射到列中来解决此问题。

有点棘手,但发布您返回大列表的完整查询,我将演示如何转置它。

【讨论】:

    猜你喜欢
    • 2011-07-16
    • 1970-01-01
    • 2020-06-14
    • 2021-08-20
    • 1970-01-01
    • 1970-01-01
    • 2019-02-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多