【问题标题】:SQL query left joining issueSQL查询离开加入问题
【发布时间】:2016-04-27 12:56:33
【问题描述】:

我有两张桌子:usersuserlogs

table1
----------
id | name
----------
1 | A
2 | B
3 | C
4 | D


table2
----------
user_id | date
----------
1 | 2015-12-17
2 | 2015-12-18
3 | 2015-12-19
4 | 2015-12-20

如果我对选定日期执行 LEFT JOIN,它只会给出表 2 中存在的数据。

SELECT r.user_id, count(r.user_id) as count
FROM table1 a LEFT OUTER JOIN table2 r ON ( r.user_id = a.id )
WHERE r.created_at BETWEEN '2015-12-17' AND '2015-12-19'
GROUP BY a.id

我得到以下内容

Result
----------
user_id | count
----------
1 | 1
2 | 1
3 | 1

我需要类似以下的东西:

Result
----------
user_id | count
----------
1 | 1
2 | 1
3 | 1
4 | 0

我尝试了很多不同的方法,但都不成功。

【问题讨论】:

  • MySQL 还是 MS SQL Server? (不要标记未涉及的产品。其中一些产品的行为很奇怪......)
  • LEFT JOIN 时,将右侧表条件从 WHERE 移动到 ON 子句,得到真正的左连接结果。 (否则它就像一个常规的内部连接。)
  • 一般的 GROUP BY 规则:“如果指定了 GROUP BY 子句,则 SELECT 列表中的每个列引用必须标识一个分组列或作为集合函数的参数。”

标签: mysql sql sql-server join left-join


【解决方案1】:

尝试将过滤条件移动到左连接中。

SELECT 
   a.id, 
   count(r.user_id) as count
FROM table1 a 
LEFT OUTER JOIN table2 r ON r.user_id = a.id AND 
                            r.created_at BETWEEN '2015-12-17' AND '2015-12-19'
GROUP BY a.id

【讨论】:

  • 感谢您的回答,但它不起作用。它给出了 NULL
  • 我只是按照你的描述设置了测试表(假设你的意思是有一个名为 created_at 而不是 date 的列)它对我有用
【解决方案2】:

如果 WHERE 子句中的过滤列使用 LEFT OUTER JOIN 指定,则 Join 的行为与 INNER JOIN 完全相同。如果您不希望将 LEFT JOIN 的行为更改为 INNER JOIN,请选择 ON & AND 条件。

    SELECT * FROM TABLE1 T1
    LEFT OUTER JOIN TABLE2 T2 ON T1.ID = T2.USER_ID
    WHERE T2.DATE BETWEEN '2015-12-17' AND '2015-12-19'

下同

    SELECT * FROM TABLE1 T1
    INNER JOIN TABLE2 T2 ON T1.ID = T2.USER_ID
    WHERE T2.DATE BETWEEN '2015-12-17' AND '2015-12-19'

在你的情况下,你需要去

    SELECT * FROM TABLE1 T1
    LEFT OUTER JOIN TABLE2 T2 ON T1.ID = T2.USER_ID 
    AND T2.DATE BETWEEN '2015-12-17' AND '2015-12-19'

【讨论】:

    【解决方案3】:

    这个查询应该没问题:

    DECLARE @T1 TABLE
    (
        id INT
        , name VARCHAR(50)
    );
    
    DECLARE @T2 TABLE
    (
        [user_id] INT
        , [date] DATE
    );
    
    INSERT INTO @T1 (id, name)
    VALUES (1, 'A'), (2, 'B'), (3, 'C'), (4, 'D');
    
    INSERT INTO @T2 ([user_id], [date])
    VALUES (1, '2015-12-17'), (2, '2015-12-18'), (3, '2015-12-19'), (4, '2015-12-20');
    
    SELECT T1.id, COALESCE(T2.Cnt, 0) AS Cnt
    FROM (SELECT DISTINCT id FROM @T1) AS T1
    LEFT JOIN (
        SELECT [user_id], COUNT(*) AS Cnt
        FROM @T2
        WHERE [date] BETWEEN '2015-12-17' AND '2015-12-19'
        GROUP BY [user_id]
        ) AS T2
        ON T2.[user_id] = t1.id;
    

    它输出你所期望的:)

    或者将它包装在这样的存储过程中:

    CREATE PROCEDURE dbo.YourProcedure
    (
        @company_id INT
        , @start_date DATE
        , @end_date DATE
    )
    as
    SELECT T1.id
        , T1.department_id
        , COALESCE(T2.cnt, 0) AS cnt
    FROM (
        SELECT DISTINCT id, department_id
        FROM users
        WHERE company_id = @company_id
        ) AS T1
    LEFT JOIN (
        SELECT user_id, COUNT(*) AS cnt
        FROM userlogs
        WHERE created_at BETWEEN @start_date AND @end_date
        GROUP BY user_id
        ) AS T2
        ON T2.user_id = t1.id;
    

    只需将变量传递给它即可调用它。 Using a stored procedure in Laravel 4 解释了如何做到这一点。

    【讨论】:

    • 您的查询似乎效率不高,为什么要创建临时表?
    • 能不能简单点?
    • @user4499992 我正在为您提供给我们的测试数据创建临时表。使用您的表格而不是 @T1@T2
    • SELECT T1.id, T1.department_id, COALESCE(T2.cnt, 0) AS cnt FROM (SELECT DISTINCT id, department_id FROM users WHERE company_id = '33') AS T1 LEFT JOIN (SELECT user_id , COUNT(*) AS cnt FROM userlogs WHERE created_at BETWEEN '2015-12-17' AND '2016-01-12' GROUP BY user_id ) AS T2 ON T2.user_id = t1.id
    • 我们可以让它简单吗
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多