【问题标题】:SQL Joining - Need single rowSQL Joining - 需要单行
【发布时间】:2012-07-05 21:37:29
【问题描述】:

我的 MySQL 表结构由两个类似如下的表组成:

UserId field1 field2
1       XXX    YYY
2       ABC    DDD
3       EEE    FFF
.
.
.
n       GGG    DDD


UserId  attribute
1        blah
1        adfadf 
1        ddad
2        adff
3        adfa
3        sdff
.
.
.
z        adss

表 1 每个 ID 只有 1 条记录。表 2 每个 id 有 M 条记录。表 1 和表 2 中的 id 是相关的。我的查询类似于以下内容:

SELECT
    t1.`UserId`,
    t1.`field1`,
    t1.`field2`,
    t2.`attribute`
FROM `table1` AS `t1`
    LEFT JOIN `table2` AS `t2` ON `t1`.UserId = `t2`.UserId
WHERE t1.`UserId` = 3

查询返回以下内容:

3 EEE FFF adfa
3 EEE FFF sdff

我不想为每个返回重复记录。我想返回一行,比如

3 EEE FFF (adfa,sdff)

第 4 个字段是一个数据结构(数组?结果集?),它包含为给定用户 ID 返回的所有属性。

编辑::: 我看到许多涉及 CONCAT 的解决方案,将其转换为 CSV。有逗号的字段会不会搞砸?

【问题讨论】:

  • 你用的是什么关系型数据库?MySQL?
  • 抱歉忘了说。 MySQL
  • 唯一可以存在于单个字段中的数据结构是原始数据类型。在这种情况下,您最终使用逗号分隔的字符串是有意义的。您不能在其中放置数组,也不能在其中嵌套数据集。正如您已经拥有的那样,关系数据库通过多行数据完成您想要的工作。在关系数据库中,将多个值聚合到一个字段中被认为是一种非常痛苦的反模式。它使以多种不同方式查询数据变得痛苦。如果我可能会问,为什么您需要将所有内容汇总到一行中?
  • @user974896 - 我想我可能不清楚。单独行上的单独值 = good。将值组合成一个字段 = bad。当前结构是关系数据库中的正常最佳实践,我绝不会推荐 JSON_ENCODED 字符串选项。我的建议是根本不要这样做,除非你有特定的理由需要这样做。如果您确实有一个,我会很想知道它,因为我们可能会评论这种方法是否真的有用。而且通常尝试按照你的要求去做没有从长远来看是有帮助的。
  • @user974896 - 带有逗号的值被聚合到一个逗号分隔的列表中只是我说你应该这样做的一个示例。

标签: php sql join left-join


【解决方案1】:

我的cmets总结:

可以存在于单个字段中的唯一数据结构是原始数据类型。

在这种情况下,您最终会使用逗号分隔的字符串。您不能在其中放置数组,也不能在其中嵌套数据集。正如您已经拥有的那样,关系数据库通过多行数据完成您想要的工作。将多个值聚合到一个字段中被认为是关系数据库中非常痛苦的反模式。以如此多不同的方式查询数据会很痛苦。

简而言之:

  • 单独行上的单独值 = good
  • 将值组合成一个字段 = bad

当前结构是关系数据库中的常规最佳实践,我绝不会推荐 JSON_ENCODED 字符串选项。我的建议是根本不要这样做,除非你有特殊的理由需要这样做。如果您确实有一个,我会很想知道它,因为我们可能会评论这种方法是否真的有用。从长远来看,通常尝试按照您的要求去做是没有帮助的。

根据您的示例,如果您有两个已经包含逗号的值会怎样?就是自找麻烦。


如果您真的担心不必要的网络流量,请分别从两个表中选择并在嵌套循环中处理它们。 *(只需确保两个数据集以相同的方式[在本例中按 user_id] 排序,以使嵌套循环尽可能简单。)*

【讨论】:

  • 抱歉回复晚了。就 JSON_ENCODING 是一件好事而言,我想到了一个例子。对于我前段时间做的一个 webapp (api),我需要存储最后 10 个查看配置文件的访问者。我会简单地拉和 JSON_DECODE 对象并将其变成滚动队列。我会排队\deque 和更新。当我想存储一个数组并出于某种原因保留订单时也可以使用。非常简单,我不必做任何复杂的逻辑。我还保存了几个查询。我知道我不必对这些字段进行反向查找。
【解决方案2】:
SELECT
    t1.`UserId`,
    t1.`field1`,
    t1.`field2`,
    CONCAT'(',GROUP_CONCAT(DISTINCT t2.`attribute`SEPARATOR ','),')') AS Attributes
FROM `table1` AS `t1`
    LEFT JOIN `table2` AS `t2` ON `t1`.UserId = `t2`.UserId
WHERE t1.`UserId` = 3
GROUP BY  t1.`UserId`,t1.`field1`,t1.`field2`

【讨论】:

    【解决方案3】:

    不确定这是否适用于您的 SQL 变体。在 T-SQL 中,这可以通过引入局部变量来实现。这个小样本演示了。如果您不熟悉,“WITH”子句是一个“公用表表达式”,可以轻松地为我提供示例数据。

    declare @temp as varchar(10);
    select @temp = '';
    
    with vals as (
        select '1' as a
        union select '2' as a
    )
    select @temp = @temp +  a + ','
    from vals
    
    select '(' + @temp + ')'
    

    对于您的查询,只需将其更改为 select @temp = @temp + attribute。当然,您需要添加一些子字符串代码来删除最后的额外逗号。

    【讨论】:

    • 不幸的是,OP 在您编写此答案时提到这是 MySQL。
    【解决方案4】:

    如果您使用的是 mysql,一种方法是使用 GROUP_CONCAT

    【讨论】:

      【解决方案5】:

      如果你使用mysql,试试GROUP_CONCAT。如果这适合,请注意 GROUP_CONCAT 有一个最大长度。您可以使用会话变量“group_concat_max_len”进行调整。

      【讨论】:

        【解决方案6】:

        您需要使用 group_concat 运算符进行连接和分组:

        SELECT t1.`UserId`, t1.`field1`, t1.`field2`,
               concat('(', group_concat(t2.`attribute`, ','), ')')
        FROM `table1` `t1` LEFT JOIN
             `table2` AS `t2`
             ON `t1`.UserId = `t2`.UserId
        WHERE t1.`UserId` = 3 
        group by t1.`UserId`, t1.`field1`, t1.`field2`
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-02-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多