【问题标题】:How to write correct sql with left join on some tables?如何在某些表上使用左连接编写正确的 sql?
【发布时间】:2014-02-09 13:22:27
【问题描述】:

美好的一天。

STRUCTURE TABLES AND ERROR WHEN EXECUTE QUERY ON SQLFIDDLE

我有一些 sql 查询:

第一个查询:

SELECT 
            n.Type AS Type,
            n.UserIdn AS UserIdn,
            u.Username AS Username,
            n.NewsIdn AS NewsIdn,
            n.Header AS Header,
            n.Text AS Text,
            n.Tags AS Tags,
            n.ImageLink AS ImageLink,
            n.VideoLink AS VideoLink,
            n.DateCreate AS DateCreate
        FROM News n 
        LEFT JOIN Users u ON n.UserIdn = u.UserIdn 

第二个查询:

SELECT 
    IFNULL(SUM(Type = 'up'),0) AS Uplikes, 
    IFNULL(SUM(Type = 'down'),0) AS Downlikes,
    (IFNULL(SUM(Type = 'up'),0) - IFNULL(SUM(Type = 'down'),0)) AS SumLikes
FROM JOIN Likes
WHERE NewsIdn=NewsIdn  //only for example- in main sql NewsIdn = value NewsIdn from row table News
ORDER BY UpLikes DESC

和树查询

SELECT 
count(*) as Favorit
Form Favorites
WHERE NewsIdn=NewsIdn  //only for example- in main sql NewsIdn = value NewsIdn from row table News

我想结合这两个查询,显示表News 中的所有行,以及UplikesDownLikes 的数量和Favorit 的数量对于每个值NewsIdn 的表News(即UplikesDownLikes 的数量和Favorit 每行News 的数量)并制作order by Uplikes Desc

请告诉我怎么做?

P.S.:结果我想要下一个值

TYPE  USERIDN USERNAME NEWSIDN HEADER TEXT                      TAGS     IMAGELINK       VIDEOLINK DATECREATE                      UPLIKES DOWNLIKES SUMLIKES FAVORIT
image 346412  test     260806  test                                      1388152519.jpg            December, 27 2013 08:55:27+0000 2       0          2       2
image 108546  test2    905554  test2  1231231111111111111111111 123. 123 1388153493.jpg            December, 27 2013 09:11:41+0000 1       0          1       0
text  108546  test2    270085  test3                            123 .123                           December, 27 2013 09:13:30+0000 1       0          1       0
image 108546  test2    764955  test4                                     1388192300.jpg            December. 27 2013 19:58:22+0000 0       1         -1       0

【问题讨论】:

    标签: mysql sql join left-join


    【解决方案1】:
    SELECT 
                n.Type AS Type,
                n.UserIdn AS UserIdn,
                u.Username AS Username,
                n.NewsIdn AS NewsIdn,
                n.Header AS Header,
                n.Text AS Text,
                n.Tags AS Tags,
                n.ImageLink AS ImageLink,
                n.VideoLink AS VideoLink,
                n.DateCreate AS DateCreate,
                IFNULL(SUM(Likes.Type = 'up'),0) AS Uplikes, 
                IFNULL(SUM(Likes.Type = 'down'),0) AS Downlikes,
               (IFNULL(SUM(Likes.Type = 'up'),0) - IFNULL(SUM(Likes.Type = 'down'),0)) AS SumLikes,
                COUNT(DISTINCT Favorites.id) as Favorit
    
            FROM News n 
            LEFT JOIN Users u ON n.UserIdn = u.UserIdn 
            LEFT JOIN Likes ON Likes.NewsIdn = n.NewsIdn
            LEFT JOIN Favorites ON n.NewsIdn=Favorites.NewsIdn
    GROUP BY n.NewsIdn
    

    【讨论】:

    • 请参阅sqlfiddle.com/#!2/35c37/1 计数问题 Favorit (不正确的值 Favorit 因为 NewsIdn = '260806' COUNT(Favorites.id) as Favorit 将为 2,但查询显示 4)
    【解决方案2】:

    首先,您的表结构包含 varchar(30) 的所有“Idn”。看起来这些实际上是其他表的 ID 键,并且应该是整数,以获得更好的索引和连接性能。

    其次,这种类型的过程,尤其是基于 Web 的过程,是通过实际将喜欢、不喜欢和收藏的值直接作为记录(例如:新闻表)中的计数器来对喜欢、不喜欢和收藏的值进行去规范化的完美示例。当一个人喜欢、不喜欢或将其作为最爱时,请立即标记并完成它。如果您是第一次执行批量 sql-update 操作,但表上也有触发器以自动处理更新计数。这样,您只需直接查询表并按您需要的顺序进行排序,而无需查询所有喜欢 +/- 记录到所有新闻中并查看哪个是最好的。在新闻表上建立索引将是您的最佳选择。

    现在,也就是说,使用您现有的表结构,您可以通过预聚合查询并将它们作为别名加入 sql FROM 子句中......类似

    SELECT
          N.Type,
          N.UserIdn,
          U.UserName,
          N.NewsIdn,
          N.Header,
          N.Text,
          N.Tags,
          N.ImageLink,
          N.VideoLink,
          N.DateCreate,
          COALESCE( SumL.UpLikes, 0 ) as Uplikes,
          COALESCE( SumL.DownLikes, 0 ) as DownLikes,
          COALESCE( SumL.NetLikes, 0 ) as NetLikes,
          COALESCE( Fav.FavCount, 0 ) as FavCount
       from
          News N
             JOIN Users U
                ON N.UserIdn = U.UserIdn
             LEFT JOIN ( select 
                               L.NewsIdn,
                               SUM( L.Type = 'up' ) as UpLikes,
                               SUM( L.Type = 'down' ) as DownLikes,
                               SUM( ( L.Type = 'up' ) - ( L.Type = 'down' )) as NetLikes
                            from
                               Likes L
                            group by
                               L.NewsIdn ) SumL
                ON N.NewsIdn = SumL.NewsIdn
             LEFT JOIN ( select 
                               F.NewsIdn,
                               COUNT(*) as FavCount
                            from
                               Favorites F
                            group by
                               F.NewsIdn ) Fav
                ON N.NewsIdn = Fav.NewsIdn
       order by
          SumL.UpLikes DESC
    

    同样,我不明白为什么新闻表会有一个自动递增的数字 ID 列,然后将另一个值作为 NewsIdn 作为 varchar。我只想让这个和你的其他表直接引用 News.ID 列......为什么有两列代表同一个组件。很明显,你正在做聚合(喜欢、收藏)的每个表都应该有关于你将加入或聚合在(因此是 NewsIdn)列、UserIdn 等的任何此类标准的索引。

    最后提醒一下,这种类型的查询总是针对您的整个点赞表、收藏夹每次运行聚合,并建议在有人选择它们时使用非规范化列来保存计数。如果您想要显示或更新某个特定的人以更改他们的喜欢/不喜欢/最喜欢的状态,您可以随时返回原始表格。

    您必须考虑阅读触发器,因为每个数据库都有自己的处理语法。

    至于表结构,这是我所拥有的简化版本(从您的 SQLFiddle 示例中删除了许多其他列)

    CREATE TABLE IF NOT EXISTS `News` (
     id int(11) NOT NULL AUTO_INCREMENT,
     UserID integer NOT NULL,
     ... other fields
     `DateCreate` datetime NOT NULL,
     PRIMARY KEY ( id ),
     KEY ( UserID )  
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;
    

    用户 ID 上的额外键,以防您希望由特定用户创建所有新闻活动。

    CREATE TABLE IF NOT EXISTS `Users` (
      id int(11) NOT NULL AUTO_INCREMENT,
      other fields...
      PRIMARY KEY ( id ),
      KEY ( LastName, Name )
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;
    

    附加键,以防您想按用户名进行搜索

    CREATE TABLE IF NOT EXISTS `Likes` (
     id int(11) NOT NULL AUTO_INCREMENT,
     UserId integer NOT NULL,
     NewsID integer NOT NULL,
     `Type` enum('up','down') NOT NULL,
     `IsFavorite` enum('yes','no') NOT NULL,
     `DateCreate` datetime NOT NULL,
     PRIMARY KEY (`id`),
     KEY ( UserID ),
     KEY ( NewsID, IsFavorite )
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;
    

    此处用于加入和/或聚合的附加键。我还添加了一个标志列,以便成为我的最爱。这可以避免需要收藏夹表,因为它们包含与 LIKES 相同的基本内容。因此,某人可以只喜欢/不喜欢给定的新闻项目,但也可以喜欢/不喜欢它作为最终用户希望能够快速参考的最爱。

    现在,如何简化这些表结构以进行查询?每个表都有自己的“id”列,但任何 OTHER 表都使用 tableNameID(UserID、NewsID、LikesID 或其他),这就是连接。

    select ...
       from 
          News N
             Join Users U
                on N.UserID = U.ID
             Join Likes L
                on N.ID = L.NewsID
    

    在编写查询时,整数列更容易被其他人识别......这是否更有意义?

    【讨论】:

    • 谢谢,请告诉我更多关于我的表格结构的信息?我使用 NewsIdn 客栈所有从这个 NewsIdn 获得价值 Likes 或 Favorit 的表。请告诉我我应该如何改变我的表格结构?
    • 我现在可以说,我可以在表格中创建触发器,并且所有值都保存在表格新闻中?我对吗? PS:触发器+1,我不知道。
    • @user3213826,查看修改后的答案。
    • 我可以为表点赞添加KEY ( NewsID, Type ), 吗?
    • @user3213826,是的,但是应该根据您可能要查询的内容添加键以帮助优化,包括在构建“覆盖索引”时的某些时候,但您也必须阅读该内容.
    猜你喜欢
    • 1970-01-01
    • 2015-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-03
    • 1970-01-01
    • 1970-01-01
    • 2020-07-25
    相关资源
    最近更新 更多