Choose where the user buys each year
I have a table sales
that stores all sales. This table has columns of type year_ordered
, userId
, orderId
etc.
I want to write a SQL query to select rows where the user has ordered every year since 2008. So I only want those who have been loyal and ordered from 2008 to 2014.
I tried with this query, but it gave me anything where year_ordered
more than 2007.
select COUNT(*) as sales_count, ss.userID, ss.year_ordered
from subscriber_sub ss
where ss.date_deleted is null
and ss.year_ordered > 2007
group by ss.year_ordered, ss.userID
having COUNT(*) > 1
order by ss.year_ordered
source to share
What you are aiming for is called relational division. There are two ways to do this:
select COUNT(distinct ss.year_ordered) as sales_count, ss.userID
from subscriber_sub ss
where ss.date_deleted is null
and ss.year_ordered > 2007
group by ss.userID
having COUNT(distinct ss.year_ordered) >= ( select 2014 - 2008 )
Another way is to rewrite FORALL x: p (x) <=> NOT EXISTS x: NOT p (x), that is, users where it hasn't existed for a year, so no sales are sold this year. I'll leave this as an exercise :-)
source to share
this should list the subscriber entries of users who have purchased more than one item from 2007 to ...
select s2.*
from subscriber_sub s2
where s2.year_ordered > 2007
and s2.userID in
(
select ss.userID
from subscriber_sub ss
where ss.date_deleted is null
and ss.year_ordered > 2007
group by ss.userID
having COUNT(*) > 1
)
source to share
This should work dynamically i.e. keep working out the number of years the user should have done based on the current time and time:
DECLARE @subscriber_sub TABLE (
userId INT,
year_ordered INT,
date_deleted DATE);
INSERT INTO @subscriber_sub VALUES (1, 2007, NULL);
INSERT INTO @subscriber_sub VALUES (1, 2008, NULL);
INSERT INTO @subscriber_sub VALUES (1, 2009, NULL);
INSERT INTO @subscriber_sub VALUES (1, 2010, NULL);
INSERT INTO @subscriber_sub VALUES (1, 2011, NULL);
INSERT INTO @subscriber_sub VALUES (1, 2012, NULL);
INSERT INTO @subscriber_sub VALUES (1, 2013, NULL);
INSERT INTO @subscriber_sub VALUES (1, 2014, NULL);
INSERT INTO @subscriber_sub VALUES (2, 2007, NULL);
INSERT INTO @subscriber_sub VALUES (2, 2008, NULL);
INSERT INTO @subscriber_sub VALUES (2, 2009, NULL);
INSERT INTO @subscriber_sub VALUES (2, 2010, NULL);
INSERT INTO @subscriber_sub VALUES (2, 2011, NULL);
INSERT INTO @subscriber_sub VALUES (2, 2012, NULL);
INSERT INTO @subscriber_sub VALUES (2, 2013, NULL);
WITH YearsOrdered AS (
SELECT
userId,
COUNT(DISTINCT year_ordered) AS years
FROM
@subscriber_sub
WHERE
year_ordered > 2007
AND date_deleted IS NULL
GROUP BY
userId)
SELECT
ss.userID,
ss.year_ordered,
COUNT(*) AS sales_count
FROM
@subscriber_sub ss
LEFT JOIN YearsOrdered yo ON yo.userId = ss.userId
WHERE
ss.date_deleted IS NULL
AND ss.year_ordered > 2007
AND yo.years = DATEDIFF(YEAR, '20070101', GETDATE())
GROUP BY
ss.year_ordered,
ss.userID
ORDER BY
ss.year_ordered,
ss.userId;
When I tested User # 1, but User # 2 was not what he had not ordered in 2014.
source to share
If you only need to know the users, you need to select userId
with counting different values year_ordered
= 7 (2008 to 2014 including):
select COUNT(*) as sales_count, ss.userID
from subscriber_sub ss
where ss.date_deleted is null
and ss.year_ordered > 2007
group by ss.userID
having COUNT( DISTINCT ss.year_ordered) = 7
source to share