1

I have two tables: users and userlogs

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

If I do LEFT JOIN with selected date, it only gives data that exist in table 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

I get the following

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

I need something like the following:

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

I have tried many different ways, but was unsuccessful.

njachowski
  • 927
  • 5
  • 14
Malarivtan
  • 404
  • 1
  • 6
  • 20
  • 2
    MySQL or MS SQL Server? (Don't tag products not involved. Some of them have a odd group by behavior...) – jarlh Jan 21 '16 at 10:24
  • 1
    When LEFT JOIN, move the right side table conditions from WHERE to ON clause to get true left join result. (Else it behaves as a regular inner join.) – jarlh Jan 21 '16 at 10:26
  • The general GROUP BY rule: "If a GROUP BY clause is specified, each column reference in the SELECT list must either identify a grouping column or be the argument of a set function." – jarlh Jan 21 '16 at 10:26

3 Answers3

2

Try moving the filter condition into the left join.

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
Magnus
  • 45,362
  • 8
  • 80
  • 118
  • Thank you for your answer, but its not working. it gives NULL – Malarivtan Jan 21 '16 at 10:41
  • I just set up the test tables as you described (assuming that you mean to have a column called created_at rather than called date) and it works for me – Kickstart Jan 21 '16 at 10:55
1

If your filtered column in WHERE clause specified with LEFT OUTER JOIN then the Join behave exactly same as INNER JOIN. If you do not wish to change the behaviour of LEFT JOIN to INNER JOIN then go for ON & AND condition.

    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'

Is same as below

    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'

In your case you need to go for

    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'
Shakeer Mirza
  • 5,054
  • 2
  • 18
  • 41
0

This query should do just fine:

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;

It outputs what you are expecting :)

Or wrap it up a in a stored procedure like that:

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;

And just call it by passing variables to it. Using a stored procedure in Laravel 4 explains how to do it.

Community
  • 1
  • 1
Evaldas Buinauskas
  • 13,739
  • 11
  • 55
  • 107