Query with multiple AND conditions - mysql

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;

Related

SQL how to aggregate by date and multiple criteria

I have this table:
id
amount
method
date
01
10
A
2022-01-24 12:27:14.440
02
80
A
2022-01-24 12:27:14.440
01
20
D
2022-02-24 12:27:14.440
01
10
D
2022-02-24 12:27:14.440
02
20
D
2022-02-24 12:27:14.440
03
30
D
2022-02-24 12:27:14.440
and I want this:
method
amount_sum_jan
n_transaction_jan
n_customer_jan
amount_sum_feb
n_transaction_feb
n_customer_feb
A
10
2
2
0
0
0
D
0
0
0
80
4
3
This is a table with 7 column and rows equal to the number of methods.
AMOUNT: sum of amount in one month of one method
N_TRANSACTIONS: number of transaction in one month with one method
N_CUSTOMER: number of customers (id) who used that method in one month
Can I get it with just one query?
You want to aggregate your data by method and have separate columns for January data and February data. You get this with conditional aggregation (CASE expression inside the aggregate function),
select
method,
sum(case when month(date) = 1 then amount else 0 end) as amount_sum_jan,
count(case when month(date) = 1 then 1 end) as n_transaction_jan,
count(distinct case when month(date) = 1 then id end) as n_customer_jan,
sum(case when month(date) = 2 then amount else 0 end) as amount_sum_feb,
count(case when month(date) = 2 then 1 end) as n_transaction_feb,
count(distinct case when month(date) = 2 then id end) as n_customer_feb
from mytable
group by method
order by method;
It is called pivot, and would for fix dates look like this.
An aggregation of the method for the year and month, and you can COUNT or SUM your number
select
`method`,
SUM(CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202201' then `amount` ELSE 0 END) amount_sum_jan,
SUM(CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202201' then 1 ELSE 0 END) n_transaction_jan,
COUNT(DISTINCT CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202201' then `d` ELSE 0 END) n_customer_jan,
SUM(CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202202' then `amount` ELSe 0 END) amount_sum_feb,
SUM(CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202202' then 1 ELSe 0 END) n_transaction_feb,
COUNT(DISTINCT CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202202' then `d` ELSe 0 END) n_customer_feb
from tab1
GROUP BY `method`
http://www.sqlfiddle.com/#!9/31d8ef/10
much more interesting would be to make that dynamic

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.

Mysql select based on other select

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.

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/