【问题标题】:Join operation duplication加入操作重复
【发布时间】:2011-04-17 14:41:59
【问题描述】:

假设我们有两个表:Users (UserId, UserName, UserPhoto) 和 Articles (ArticleId, UserId, ArticleText)。现在我们执行内部连接查询来检索有文章的用户:

SELECT UserId, UserName, UserPhoto, ArticleId, ArticleText 
FROM Users as u INNER JOIN Articles as a ON u.UserId = a.UserId

查询结果的结构如下:

UserId1 UserName1 UserPhoto1 ArticleId1 ArticleText1
UserId1 UserName1 UserPhoto1 ArticleId2 ArticleText2

所以对于第一个用户,我们有两篇文章,并且 UserName1 和 UserPhoto1 是重复的。如果 UserPhoto 存储几个 GB 的 blob 会怎样?

我希望数据库协议对这种情况有一些优化(可能是一些映射告诉 UserPhoto 对于第一行和第二行是相等的),但我从未遇到过任何关于此的注释。所以我只是想确定这种优化存在,我不需要自己解决它

【问题讨论】:

    标签: mysql sql database join


    【解决方案1】:

    首先,为 Photos 创建第三个表,并将 UserId 与 Photo 相关联。其次,您需要运行两个单独的查询才能检索:

    1. 用户提交的每张照片
    2. 每篇文章都与特定用户/照片相关联

    您将遍历所有用户/照片对,并查询循环内的文章。

    【讨论】:

    • 您在谈论解决方法(这需要几个数据库查询),但我问的是数据库及其提供者使用的解决方案。我只是不相信这种重复没有任何内置解决方案
    【解决方案2】:

    您可以运行两个查询,一个用于获取用户数据(因此每张照片将移动一次):

    SELECT u.UserId
         , u.UserName
         , u.UserPhoto
    FROM Users as u
    

    另一个获取其余(文章)数据:

    SELECT a.UserId               <--- only UserId this time
         , a.ArticleId
         , a.ArticleText 
    FROM Users as u
      INNER JOIN Articles as a
        ON u.UserId = a.UserId
    

    最后,使用用户 ID 将结果合并到您的应用程序代码中。

    【讨论】:

    • 同意。如果要确保最佳数据流,则必须运行不同的查询。如果你有一个 BLOB,它会流动 n 次,不管它是否相同。
    【解决方案3】:

    您可以避免像这样多次获取照片:

    SELECT * FROM (
      SELECT UserId, UserName, UserPhoto, ArticleId, ArticleText 
        FROM Users as u INNER JOIN Articles as a ON u.UserId = a.UserId
        WHERE ArticleId IN (SELECT MIN(ArticleId) FROM Articles GROUP BY UserId)
      UNION ALL
      SELECT UserId, UserName, NULL, ArticleId, ArticleText
      FROM Users as u INNER JOIN Articles as a ON u.UserId = a.UserId
      WHERE ArticleId NOT IN (SELECT MIN(ArticleId) FROM Articles GROUP BY UserId)
    ) base
    ORDER BY ArticleId;  // UserId,ArticleId will also work if you want it sorted by users.
    

    这仅获取获取第一篇文章的照片,并为后续文章返回 NULL。您的应用程序可以在第一次读取时缓存照片。

    【讨论】:

      【解决方案4】:

      1) 无论 photoblob 在您的结果集中出现多少次,它只会被读取一次(从磁盘到服务器中的内存),内置优化以确保这种情况发生。

      2) 但是它可以多次传输(从服务器到客户端),没有内置优化。

      3) 最好的解决方案是将其包装为一个返回 2 个记录集的存储过程,然后在 clinet 代码中进行连接,这种方法不同于运行需要两次往返的 2 个查询。

      4) 如果您不想这样做,您可以以 CSV 格式获取用户的所有文章 ID,然后您可以轻松地将 csv 拆分为客户端代码中的单独字符串。

      这是示例输出

      UserId  UserName  UserPhoto   CSV_ArticleId               CSV_ArticleText
      ------- --------- ----------  ------------------------    ----------------------------
      UserId1 UserName1 UserPhoto1  ",ArticleId1,ArticleId2"    ",ArticleText1,ArticleText2"
      UserId2 UserName2 UserPhoto2  ",ArticleId3"               ",ArticleText3"
      

      您可以这样做。在测试数据库上逐字运行代码,您可以看到结果

      CREATE TABLE Users(UserId int , UserName nvarchar(256), UserPhoto nvarchar(256))
      
      CREATE TABLE Articles (ArticleId int , UserId int , ArticleText nvarchar(256))
      
      INSERT INTO Users(UserId,UserName,UserPhoto)
      VALUES (2,'2a','2pa')
      INSERT INTO Users(UserId,UserName,UserPhoto)
      VALUES (1,'a','pa')
      
      INSERt INTO Articles (ArticleId, UserId, ArticleText)
      VALUES (2,2,'text2')
      INSERt INTO Articles (ArticleId, UserId, ArticleText)
      VALUES (1,2,'text1')
      
      ;WITH tArticles AS (SELECT ArticleId, UserId, ArticleText FROM Articles)
      SELECT 
          UserId, 
          UserName, 
          UserPhoto,
          (SELECT TOP 1 LTRIM(
                              (SELECT ',' + CONVERT(nvarchar(256),A.ArticleId) FROM Articles A WHERE U.UserId = A.UserId ORDER BY A.ArticleId FOR XML PATH(''))
                              )) as CSV_ArticleId,
          (SELECT TOP 1 LTRIM(
                              (SELECT ',' + CONVERT(nvarchar(256),A.ArticleText) FROM Articles A WHERE U.UserId = A.UserId ORDER BY A.ArticleId FOR XML PATH(''))
                              )) as CSV_ArticleText                   
      
      FROM Users U
      

      【讨论】:

        猜你喜欢
        • 2017-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-03
        • 2017-03-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多