Mysql select based on other select - mysql

dears i have below query that's gets the count based on cases and it's working fine
select users.firstName,users.lastName,users.id,users.phoneNumber,
count(CASE
WHEN orders.`orderStatus` = 4 THEN 1 ELSE null END) As completed,
count(CASE
WHEN orders.`orderStatus` = 5 THEN 1 ELSE null END) as CustomerCancelled,
count(CASE
WHEN orders.`orderStatus` = 11 THEN 1 ELSE null END) as providerCancelled,
count(`orders`.`createdAt`) as total,
from users,providers,orders
where
`orders`.`providerId` = `providers`.`id`
and
users.id = `providers`.userId
and
`orders`.`createdAt` >= (CURDATE() - INTERVAL 7 DAY)
GROUP BY users.id;
what I need to add is to get the count for CustomerCancelled/total and show it with each record
I tried to divide it like below but not working
select users.firstName,users.lastName,users.id,users.phoneNumber,
count(CASE
WHEN orders.`orderStatus` = 4 THEN 1 ELSE null END) As completed,
count(CASE
WHEN orders.`orderStatus` = 5 THEN 1 ELSE null END) as CustomerCancelled,
count(CASE
WHEN orders.`orderStatus` = 11 THEN 1 ELSE null END) as providerCancelled,
count(`orders`.`createdAt`) as total,
CustomerCancelled/total //// this is what i tried to do
from users,providers,orders
where
`orders`.`providerId` = `providers`.`id`
and
users.id = `providers`.userId
and
`orders`.`createdAt` >= (CURDATE() - INTERVAL 7 DAY)
GROUP BY users.id;

You can't use aliases in the SELECT part of the query. You need to explicitly write out the expression:
select users.firstName,
users.lastName,
users.id,
users.phoneNumber,
COUNT(CASE WHEN orders.`orderStatus` = 4 THEN 1 END) aS completed,
COUNT(CASE WHEN orders.`orderStatus` = 5 THEN 1 END) AS CustomerCancelled,
COUNT(CASE WHEN orders.`orderStatus` = 11 THEN 1 END) AS providerCancelled,
COUNT(`orders`.`createdAt`) AS total,
COUNT(CASE WHEN orders.`orderStatus` = 5 THEN 1 END) / COUNT(`orders`.`createdAt`) AS ratio_cancelled
FROM users
JOIN providers
JOIN orders
WHERE `orders`.`providerId` = `providers`.`id`
AND users.id = `providers`.userId
AND `orders`.`createdAt` >= (CURDATE() - INTERVAL 7 DAY)
GROUP BY users.id;
Note that you don't need an ELSE null in your CASE expressions as that is the default. Also you should write explicit JOIN statements rather than use the deprecated comma style implicit JOIN.

Related

Query with multiple AND conditions

I have my query like below
SELECT dates,
COUNT(link_data_id) as TotalClicks,
sum(case when link_redirect_status = 1 then 1 else 0 end) AS GoodClicks,
sum(case when link_redirect_status != 1 then 1 else 0 end) AS BadClicks
FROM tbl_calendar
LEFT JOIN tbl_links_data ON dates = CAST(link_data_time AS DATE)
WHERE (`dates` BETWEEN '2021-11-28' AND DATE_ADD('2021-11-28', INTERVAL 7 DAY))
GROUP BY dates
It's giving me expected output like below
But I want add one more condition called link_order_id ='abcde', so I am trying like below
SELECT dates,
COUNT(link_data_id) as TotalClicks,
sum(case when link_redirect_status = 1 then 1 else 0 end) AS GoodClicks,
sum(case when link_redirect_status != 1 then 1 else 0 end) AS BadClicks
FROM tbl_calendar
LEFT JOIN tbl_links_data ON dates = CAST(link_data_time AS DATE)
WHERE link_order_id = 'abcde'
AND (`dates` BETWEEN '2021-11-28' AND DATE_ADD('2021-11-28', INTERVAL 7 DAY))
GROUP BY dates
But it's giving me only two rows like below
Why I am getting only two rows instead of 8 rows like first picture?
Move the criteria in the WHERE clause to the ON clause of the join:
SELECT
c.dates,
COUNT(d.link_data_id) AS TotalClicks,
SUM(CASE WHEN d.link_redirect_status = 1 THEN 1 ELSE 0 END) AS GoodClicks,
SUM(CASE WHEN d.link_redirect_status != 1 THEN 1 ELSE 0 END) AS BadClicks
FROM tbl_calendar c
LEFT JOIN tbl_links_data d
ON c.dates = CAST(d.link_data_time AS DATE) AND
d.link_order_id = 'abcde'
WHERE (c.dates BETWEEN '2021-11-28' AND DATE_ADD('2021-11-28', INTERVAL 7 DAY))
GROUP BY c.dates;

sql for Group By and SUM with date range condition

Below is the Invoices table:
I am trying to make a sql query which gives me output based on due_date range with the sum of balance_amount group by company_id
My try:
select invoices.company_id,
SUM(invoices_cmonth.balance_amount) as cmonth,
SUM(invoices_1month.balance_amount) as 1month,
SUM(invoices_2month.balance_amount) as 2month
from `invoices`
LEFT JOIN invoices invoices_cmonth
ON (invoices.company_id = invoices_cmonth.company_id and invoices_cmonth.due_date >= '2021-11-10')
LEFT JOIN invoices invoices_1month
ON (invoices.company_id = invoices_1month.company_id and invoices_1month.due_date < '2021-11-10' and invoices_1month.due_date >= '2021-10-10')
LEFT JOIN invoices invoices_2month
ON (invoices.company_id = invoices_2month.company_id and invoices_2month.due_date < '2021-10-10' and invoices_2month.due_date >= '2021-9-10')
where invoices.`status` = 'ACTIVE'
and invoices.`balance_amount` > 0
and `invoices`.`deleted_at` is null
group by invoices.`company_id`
But it is giving me wrong figures in balance amount.
I suggest just making a single pass over the invoices table using conditional aggregation for the various time windows:
SELECT
company_id,
SUM(CASE WHEN due_date >= '2021-11-10' THEN balance_amount ELSE 0 END) AS cmonth,
SUM(CASE WHEN due_date >= '2021-10-10' AND due_date < '2021-11-10'
THEN balance_amount ELSE 0 END) AS 1month,
SUM(CASE WHEN due_date >= '2021-09-10' AND due_date < '2021-10-10'
THEN balance_amount ELSE 0 END) AS 2month
FROM invoices
WHERE
status = 'ACTIVE' AND balance_amount > 0 AND deleted_at IS NULL
GROUP BY
company_id;

Display all data grouped by date in a particular timeframe

Currently I have 2 tables, a listing table and a logs table. With the following query I'm trying to get the listings of a product on a particular day, and it returns the right output.
with X as (
select
l.*,
(select status_from from logs where logs.refno = l.refno and logs.logtime >= '2021-10-01' order by logs.logtime limit 1) logstat
from listings l
where l.added_date < '2021-10-01'
)
, Y as (select X.*, ifnull(X.logstat, X.status) stat from X)
SELECT
status.text,
COUNT(Y.id) AS c
from status
left join Y on Y.stat = status.code
group by status.code, status.text;
This gives an output like this:
Here I've filtered the query by 1 date which in this case is 2021-10-01. Now I have 2 input forms where the user can select a from date and a to date. So I want to be able to get all the data between the date range provided. So basically if I choose a date between 2021-10-01 and 2021-10-02, it should show everything on and between that date. The output should look like:
Date
Publish
Action
Let
Sold
Draft
2021-10-01
0
3
0
1
1
2021-10-02
0
2
0
1
2
Dbfiddle: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=5e0b8d484a41ac9104d0fb002e7f9a3c
I've formatted the table to show the entries in a row wise manner with the following query:
with X as (
select l.*,
(select status_from from logs where logs.refno = l.refno and logs.logtime >= '2021-10-01' order by logs.logtime limit 1) logstat
from listings l
where l.added_date < '2021-10-01'
)
, Y as (select X.*, ifnull(X.logstat, X.status) stat20211001 from X)
SELECT
sum(case when status.text= 'Action' and Y.id is not null then 1 else 0 end) as `Action`,
sum(case when status.text= 'Draft' and Y.id is not null then 1 else 0 end) as `Draft`,
sum(case when status.text= 'Let' and Y.id is not null then 1 else 0 end) as `Let`,
sum(case when status.text= 'Sold' and Y.id is not null then 1 else 0 end) as `Sold`,
sum(case when status.text= 'Publish' and Y.id is not null then 1 else 0 end) as `Publish`
from status
left join Y on Y.stat20211001 = status.code
Output for this statement:
If you open my dbfiddle and enter date as 2021-10-01 it gives correct output and if you enter 2021-10-02 it shows correct output. Now I just want a way to show these both together. Also if it is suppose 2021-10-01 and 2021-10-05, it should show everything in middle too which means 2021-10-01, 2021-10-02, 2021-10-03, 2021-10-04 and 2021-10-05
Your listings.added_date column has the DATETIME data type. Therefore, to select a date range of 2021-10-01 to 2021-10-02 you need to do this.
WHERE added_date >= '2021-10-01'
AND added_date < '2021-10-02' + INTERVAL 1 DAY
This pulls in all the rows from midnight on 1-October, up to but not including midnight on 3-October.
If you want to aggregate your results by day, you can use GROUP BY DATE(added_date).
A sample query -- to show all days in September -- might look like this:
SELECT DATE(added_date) day,
SUM(CASE WHEN status.text= 'Action' THEN 1 ELSE 0 END) AS `Action`,
SUM(CASE WHEN status.text= 'Draft' THEN 1 ELSE 0 END) AS `Draft`,
SUM(CASE WHEN status.text= 'Let' THEN 1 ELSE 0 END) AS `Let`
FROM tbl
WHERE added_date >= '2021-09-01'
AND added_date < '2021-09-01' + INTERVAL 1 MONTH
GROUP BY DATE(added_date);
Sorry to say, I don't understand how your sample query works well enough to rewrite it with GROUP BY. But this should get you started.

CASE statement not working as it should in SQL

SELECT userid,
(SELECT CASE WHEN (COUNT(CASE WHEN casinowagers != 0
THEN 1
ELSE null
END)
+ COUNT(CASE WHEN depositmade_amt != 0
THEN 1
ELSE null
END)) >= 3
AND (Round(sum(totalhold - playercomps - freemoney - (depositmade_amt*.1)),2)) >= 10
THEN "VIP"
ELSE "NON-VIP"
END as VIPcheck
FROM player_activity
WHERE YEAR(txndate) = YEAR(CURRENT_DATE - INTERVAL 3 MONTH)
AND MONTH(txndate) = MONTH(CURRENT_DATE - INTERVAL 3 MONTH)
) as vipMonthStatus,
(COUNT(CASE WHEN casinowagers != 0
THEN 1
ELSE null
END)
+ COUNT(CASE WHEN depositmade_amt != 0
THEN 1
ELSE null
END)) as activityCount,
(Round(sum(totalhold - playercomps - freemoney - (depositmade_amt*.1)),2)) as Value,
FROM player_activity
WHERE userid = 2023410
GROUP BY year(txndate),month(txndate)
LIMIT 1000
So basically the vipMonth status is always returning as "VIP". However, for the month of May (because of the below "where" statement), it should be "non-vip" because only 1 deposit and 1 wager were made.
What gives?
( WHERE YEAR(txndate) = YEAR(CURRENT_DATE - INTERVAL 3 MONTH)
AND MONTH(txndate) = MONTH(CURRENT_DATE - INTERVAL 3 MONTH) )
You need to tie the player_activity table in the vipMonthStatus subquery to the player_activity table in the main query.
First alias the "inner" player_activity and the "outer" player_activity tables so that you can tell them apart. Something like in_player_activity and out_player_activity.
Then you need to add an additional WHERE statement to the vipMonthStatus subquery to restrict the records in the inner table to the associated records in the outer table based on userid . It would end up looking like this:
FROM player_activity in_player_activity
WHERE YEAR(in_player_activity.txndate) = YEAR(CURRENT_DATE - INTERVAL 3 MONTH)
AND MONTH(in_player_activity.txndate) = MONTH(CURRENT_DATE - INTERVAL 3 MONTH)
AND in_player_activity.userid = out_player_activity.userid
Keep in mind now that you have introduced table aliases, you will need to add the alias prefix to all of the other column names. Personally I would use something more concise like opa and ipa.

SQL adding Months

Hello I am trying to get records of certain months. For example for this query I would want the user to see the current month which is "May" and one month from the past which would be "April". I don't want my query to run any other month from that point on. I am stuck with this query and cant figure it out. Basically I need a function in my query to automatically know this month and the 1 month before to show the records. Thank you
DECLARE #Year int
set #Year = 2014
SELECT d.name, a.dealer_code, b.last_name, b.city, b.state, b.phone
, COUNT(CASE WHEN MONTH(c.Funded_date) = 1 THEN 1 ELSE NULL END) January
, COUNT(CASE WHEN MONTH(c.Funded_date) = 2 THEN 1 ELSE NULL END) Feburary
, COUNT(CASE WHEN MONTH(c.Funded_date) = 3 THEN 1 ELSE NULL END) March
, COUNT(CASE WHEN MONTH(c.Funded_date) = 4 THEN 1 ELSE NULL END) April
, COUNT(CASE WHEN MONTH(c.Funded_date) = 5 THEN 1 ELSE NULL END) May
, COUNT(CASE WHEN MONTH(c.Funded_date) = 6 THEN 1 ELSE NULL END) June
, COUNT(CASE WHEN MONTH(c.Funded_date) = 7 THEN 1 ELSE NULL END) July
, COUNT(CASE WHEN MONTH(c.Funded_date) = 8 THEN 1 ELSE NULL END) August
, COUNT(CASE WHEN MONTH(c.Funded_date) = 9 THEN 1 ELSE NULL END) September
, COUNT(CASE WHEN MONTH(c.Funded_date) = 10 THEN 1 ELSE NULL END) October
, COUNT(CASE WHEN MONTH(c.Funded_date) = 11 THEN 1 ELSE NULL END) November
, COUNT(CASE WHEN MONTH(c.Funded_date) = 12 THEN 1 ELSE NULL END) December
, COUNT(*) 'Year to Date'
FROM tdealer a
JOIN tContact b ON a.contact_id = b.contact_id
JOIN tContract c ON a.dealer_id = c.dealer_id
JOIN tCompany d ON c.company_id = d.company_id
where YEAR (c.Funded_date) = #Year
GROUP BY d.name, a.dealer_code, b.last_name, b.city, b.state, b.phone
The trick here is to do two things:
Create an expression which converts any arbitrary DATETIME into the first day of the month in which that date occurred.
Use it appropriately in WHERE and GROUP BY clauses.
The expression is
DATE_FORMAT(whatever, '%Y-%m-01')
This takes, for example '2014-05-28 12:22:30', and turns it into '2014-05-01'. It's really handy to convert one valid DATE to another valid DATE, because then you can use all the good date arithmetic built into MySQL.
So, for example, if you want the first of the month before the present month you use this:
DATE_FORMAT(NOW(), '%Y-%m-01') - INTERVAL 1 MONTH
Here's the outline of your query:
SELECT DATE_FORMAT(c.Funded_date, '%Y-%m-01') AS month_beginning,
d.name, a.dealer_code, b.last_name, b.city, b.state, b.phone,
COUNT(1) AS itemcount
FROM tdealer a
JOIN tContact b ON a.contact_id = b.contact_id
JOIN tContract c ON a.dealer_id = c.dealer_id
JOIN tCompany d ON c.company_id = d.company_id
where c.Funded_date >= DATE_FORMAT(NOW(), '%Y-%m-01') - INTERVAL 1 MONTH
AND c.Funded_date < DATE_FORMAT(NOW(), '%Y-%m-01') + INTERVAL 1 MONTH
GROUP BY DATE_FORMAT(c.Funded_date, '%Y-%m-01'),
d.name, a.dealer_code, b.last_name, b.city, b.state, b.phone
ORDER BY d.name, a.dealer_code, b.last_name, b.city, b.state, b.phone,
DATE_FORMAT(c.Funded_date, '%Y-%m-01')
See how you choose the two-month period? It starts with the first day of the month before the present month, and ends with the first day of next month, but without including that day.
How to do this sort of thing is written up at http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/