Get all users who placed requests in last three years - mysql

I would like to get all the list of users who placed requests in the last three years.
Requests( request_id, request_day, user_id, userprofile_id )
Am I doing it right?
SELECT user_id
FROM requests
WHERE EXTRACT(YEAR FROM request_day) IN ( 2014,2015,2016 )
GROUP BY user_id
HAVING COUNT(*) = 3;

Your current approach is almost correct. All you need to do is to count by the distinct number of years:
SELECT user_id
FROM requests
WHERE YEAR(request_day) IN (2014, 2015, 2016)
GROUP BY user_id
HAVING COUNT(DISTINCT YEAR(request_day)) = 3;
If the DISTINCT count of years is 3, then it implies that a user has all three of the years in your WHERE IN clause.
Note that another way to do this would be conditional aggregation:
SELECT user_id
FROM requests
GROUP BY user_id
HAVING SUM(CASE WHEN YEAR(request_day) = 2014 THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN YEAR(request_day) = 2015 THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN YEAR(request_day) = 2016 THEN 1 ELSE 0 END) > 0
This approach would scale better if your query were to get more complex.

Related

MySQL - join/group concat returning too many items

I've got a simple query which brings up wins, draws and losses in a head-to-head matches table.
SELECT
SUM(CASE WHEN score_w > score_m THEN 1 ELSE 0 END) AS wins_w,
SUM(CASE WHEN score_m > score_w THEN 1 ELSE 0 END) AS wins_m,
SUM(CASE WHEN score_w = score_m THEN 1 ELSE 0 END) AS draws
FROM 6dos7me3xn8
All is well. I get a single row, with the data I need as columns.
Now I want to also pull out a group concat'd list of the most recent three match dates. I tried:
SELECT
SUM(CASE WHEN mn.score_w, 0) > mn.score_m THEN 1 ELSE 0 END) AS wins_w,
SUM(CASE WHEN mn.score_m > mn.score_w THEN 1 ELSE 0 END) AS wins_m,
SUM(CASE WHEN mn.score_w = mn.score_m THEN 1 ELSE 0 END) AS draws,
GROUP_CONCAT(jn.date) AS recent
FROM 6dos7me3xn8 mn
JOIN (SELECT date FROM 6dos7me3xn8 ORDER BY date DESC LIMIT 3) jn
...but the LIMIT is having no effect, seemingly - I get all of the dates group concat'd, not just 3.
I also tried removing the JOIN and replacing the GROUP_CONCAT with
GROUP_CONCAT((SELECT date FROM 6dos7me3xn8 ORDER BY date DESC LIMIT 3)) AS recent
...but that errors with 'Subquery returns more than 1 row.'
I'm sure it's something simple, but what am I doing wrong?
If you are running MySQL 8.0, you can do this with window functions:
select
sum(score_w > score_m) as wins_w,
sum(score_m > score_w) as wins_m,
sum(score_w = score_m) as draws,
group_concat(case when rn <= 3 then date end) as recent
from (
select t.*, row_number() over(order by date desc) rn
from `6dos7me3xn8` t
) t
The subquery ranks records by descending date; we can then use that information in the outer query. Note that you don't need the case expressions: MySQL evaluates true/false conditions as 1/0 in numeric contet.
In earlier versions, the simpler approach is probably a row-limiting subquery:
select
sum(score_w > score_m) as wins_w,
sum(score_m > score_w) as wins_m,
sum(score_w = score_m) as draws,
(select group_concat(date) from (select date from `6dos7me3xn8` order by date desc limit 3) t) as recent
from `6dos7me3xn8`
You are doing a cross join. TO get the most recent three dates, you can use:
FROM (SELECT mn.*, DENSE_RANK() OVER (ORDER BY date desc) as seqnum
FROM 6dos7me3xn8 mn
) mn
WHERE seqnum <= 3
You have no ON clause to specify the relationship between the subquery and the table you're joining it to. So you get a full cross product.
You also need ORDER BY to make it return the 3 most recent dates, not any 3 dates.
SELECT
SUM(CASE WHEN mn.score_w, 0) > mn.score_m THEN 1 ELSE 0 END) AS wins_w,
SUM(CASE WHEN mn.score_m > mn.score_w THEN 1 ELSE 0 END) AS wins_m,
SUM(CASE WHEN mn.score_w = mn.score_m THEN 1 ELSE 0 END) AS draws,
GROUP_CONCAT(jn.date) AS recent
FROM 6dos7me3xn8 mn
JOIN (
SELECT DISTINCT date
FROM 6dos7me3xn8
ORDER BY date DESC
LIMIT 3
) jn ON jn.date = mn.date

Count different values in a column, while doing total and group by different column values

We have a table with data from different nodes and one of the column will have status report as "compliant or non-compliant", sample data as below
I want to filter the table in such a way that if any of the checks on a node shows non compliant, it should be flagged as non-compliant and rest as compliant. Using below query i am able to do it
SELECT COUNT(*) AS total_nodes,
SUM(fully_compliant = 0) AS Non_compliant_nodes,
SUM(fully_compliant = 1) AS compliant_nodes
FROM (
SELECT Node, CASE WHEN SUM(Status = 'Compliant') = COUNT(*) THEN 1 ELSE 0 END AS fully_compliant
FROM your_table GROUP BY Node
)
Now, i want to group and split the result by dept as below, how can i achieve this
I think you're looking for this:
select dept,
count(*) as total_nodes,
sum(case when non_compliant_chk = 0 then 1 else 0 end) as compliant_nodes,
sum(case when non_compliant_chk > 0 then 1 else 0 end) as non_compliant_nodes
from (
select dept,
node,
sum(case when 'Non-Compliant' then 1 else 0 end) as non_compliant_chk
from your_table
group by dept,
node
) v
group by dept;
With few modifications to what Brian suggested, I am able to get the desired result
select dept,
count(*) as total_nodes,
sum(case when non_compliant_chk = 0 then 1 else 0 end) as compliant_nodes,
sum(case when non_compliant_chk > 0 then 1 else 0 end) as non_compliant_nodes
from (
select dept,
node,
COUNT(CASE WHEN Compliance-Status = 'Non-Compliant' THEN 1 END) 'non_compliant_chk'
from table WHERE DOR >= DATE(NOW()) - INTERVAL 7 DAY
group by Dept,
Node
) v
group by Dept;

Grouping by months and by column with diferent values

I am having problems trying to create a query that allow me to group by months and by a column that has different values.
The following is a small representation of the table and columns I need to query.
Table name is requests and has two columns date and status. Status may have the values pending, attended, absent and canceled.
I want to get a query that looks like this
Right now i am trying a subquery on status for each possible value. It works but is a very slow query. It takes arround 48s for 8000 rows.
SELECT
MONTHNAME(date)
(SELECT count(status) FROM requests WHERE status = "pending"),
(SELECT count(status) FROM requests WHERE status = "attended"),
(SELECT count(status) FROM requests WHERE status = "absent"),
(SELECT count(status) FROM requests WHERE status = "canceled")
FROM request
GROUP BY 1;
Any recommendations on how to get the result efficiently? Thank you very much
You could use case whene on status instead of several subselect
select
MONTHNAME(date)
, sum( case when status = "pending" then 1 else 0 end) pending
, sum( case when status = "attended" then 1 else 0 end) attended
, sum( case when status = "absent" then 1 else 0 end) absent
, sum( case when status = "canceled" then 1 else 0 end) canceled
FROM request
GROUP BY MONTHNAME(date) ;

Nested SQL Query for count of months

Locked. There are disputes about this question’s content being resolved at this time. It is not currently accepting new answers or interactions.
I am new to SQL and would like to know how to approach writing a query for this question.
Lets say we have these fields:
date_created date_unsubscribed subscriberid
How to write a SQL query that lists, by month, how many people subscribed to the list, unsubscribed from the list, and how many net subscribers there were (new subscribers minus unsubscribers).
All in a single query...
Here's one option using conditional aggregation and union all:
select month(dt),
count(case when subscribe = 1 then 1 end) subscribecount,
count(case when subscribe = -1 then 1 end) unsubscribecountt,
sum(subscribe) overallcount
from (
select date_created as dt, 1 as subscribe
from yourtable
union all
select date_unsubscribed, -1
from yourtable
where date_unsubscribed is not null
) t
group by month(dt)
The subquery creates a list of dates with a flag for subscribe or unsubscribe. Then you can use count with case to determine the appropriate number of subscribers/unsubscribers.
SQL Fiddle Demo
You could write a sum(case) (a sum with conditions) to aggregate - assuming the date_created column is never null. For instance:
ORACLE:
SELECT
TO_CHAR(DATE_CREATED,'MM-YYYY') CREATE_MONTH
,SUM(CASE WHEN date_unsubscribed is not null then 1 else 0 end) unsubscribed
,SUM(CASE WHEN date_unsubscribed is null then 1 else 0 end) subscribed
,COUNT(SUBSCRIBER_ID)
FROM
--YOURTABLENAME
--WHERE
--WHATEVER OTHER CONDITIONS YOU HAVE APPLY
GROUP BY TO_CHAR(DATE_CREATED,'MM-YYYY')
MYSQL:
SELECT
DATE_FORMAT(DATE_CREATED,'%m-%Y') CREATE_MONTH
,SUM(CASE WHEN date_unsubscribed is not null then 1 else 0 end) unsubscribed
,SUM(CASE WHEN date_unsubscribed is null then 1 else 0 end) subscribed
,COUNT(SUBSCRIBER_ID)
FROM
--YOURTABLENAME
--WHERE
--WHATEVER OTHER CONDITIONS YOU HAVE APPLY
GROUP BY DATE_FORMAT(DATE_CREATED,'%m-%Y')
Oracle solution
Here is a query using the PIVOT operator, which was created exactly for this kind of work, and ROLLUP to get the net number. This is just for illustration; I assume the year is a user or application input (bind variable :year, set to 2015 for the output), and I show the summary for January through June.
with
test_data ( date_created, date_unsubscribed, subscriber_id ) as (
select date '2015-05-10', null , 330053448 from dual union all
select date '2015-04-28', null , 330053457 from dual union all
select date '2015-05-10', null , 330053466 from dual union all
select date '2015-04-28', null , 220053475 from dual union all
select date '2015-04-28', date '2015-05-10', 330053484 from dual
),
prep ( type, val, mth ) as (
select 'Subscribed' , 1, extract(month from date_created) from test_data
where extract(year from date_created) = :year
union all
select 'Unsubscribed', -1, extract(month from date_unsubscribed) from test_data
where extract(year from date_unsubscribed) = :year
)
select nvl(type, 'Net Subscr') as description,
nvl(sum(jan), 0) as jan, nvl(sum(feb), 0) as feb, nvl(sum(mar), 0) as mar,
nvl(sum(apr), 0) as apr, nvl(sum(may), 0) as may, nvl(sum(jun), 0) as jun
from prep
pivot (
sum(val)
for mth in (1 as jan, 2 as feb, 3 as mar, 4 as apr, 5 as may, 6 as jun)
)
group by rollup(type)
order by case type when 'Subscribed' then 1 when 'Unsubscribed' then 2 else 3 end
;
DESCRIPTION JAN FEB MAR APR MAY JUN
------------ ---------- ---------- ---------- ---------- ---------- ----------
Subscribed 0 0 0 3 2 0
Unsubscribed 0 0 0 0 -1 0
Net Subscr 0 0 0 3 1 0
3 rows selected.

sql group by date gives same value everyday

So I want to group by days for statistics so I have data on each day (type 1 is bought and 0 is sold) but the query gives me the same result everyday and that is not correct can someone help me with this code?
SELECT
DATE(from_unixtime(credit_transaction_time)) AS data_date,
total_spend AS credits_spend,
total_bought AS credits_bought
FROM credit_transactions
JOIN (SELECT SUM(`credit_transaction_amount`) AS total_spend FROM credit_transactions WHERE `credit_transaction_type` = 0 GROUP BY DATE(from_unixtime(credit_transaction_time))) AS spend
JOIN (SELECT SUM(`credit_transaction_amount`) AS total_bought FROM credit_transactions WHERE `credit_transaction_type` = 1 GROUP BY DATE(from_unixtime(credit_transaction_time))) AS bought
GROUP BY DATE(from_unixtime(credit_transaction_time))
Use conditional aggregation:
SELECT DATE(from_unixtime(credit_transaction_time)) AS data_date,
SUM(CASE WHEN credit_transaction_type = 0 THEN credit_transaction_amount ELSE 0 END) as credits_spend,
SUM(CASE WHEN credit_transaction_type = 1 THEN credit_transaction_amount ELSE 0 END) as credits_bought
FROM credit_transactions
GROUP BY DATE(from_unixtime(credit_transaction_time));
Your query doesn't work because you don't have an ON condition. In most databases, this would result in a syntax error, but MySQL allows this syntax.