The following query returns the correct data but I'd like to see if there's a better way of doing this. The query should return the number of cases for each month within a 12 month period where a record exists within the past 2 months. The idea is to get number of accounts that ordered during the month in question and at least one of the previous 2 months. Also, please note that every value in the table for data_date will always be the 1st of the month.
SELECT
sum(
case
WHEN a.data_date = '2013-03-01'
and exists(
select 1 from sales mth1
where mth1.client_id = a.client_id
and
data_date BETWEEN '2013-01-01'
and
'2013-02-01'
)
then case_qty
ELSE 0 END
) AS M1 ,
sum(
case
WHEN a.data_date = '2013-04-01'
and exists(
select 1 from sales mth2
where mth2.client_id = a.client_id
and mth2.data_date BETWEEN '2013-02-01'
and
'2013-03-01'
)
then case_qty
ELSE 0 END
) AS M2 ,
sum( case WHEN a.data_date = '2013-05-01' and exists( select 1 from sales mth3 where mth3.client_id = a.client_id and mth3.data_date BETWEEN '2013-03-01' and '2013-04-01' ) then case_qty ELSE 0 END) AS M3 ,
sum( case WHEN a.data_date = '2013-06-01' and exists( select 1 from sales mth4 where mth4.client_id = a.client_id and mth4.data_date BETWEEN '2013-04-01' and '2013-05-01' ) then case_qty ELSE 0 END) AS M4 ,
sum( case WHEN a.data_date = '2013-07-01' and exists( select 1 from sales mth5 where mth5.client_id = a.client_id and mth5.data_date BETWEEN '2013-05-01' and '2013-06-01' ) then case_qty ELSE 0 END) AS M5 ,
sum( case WHEN a.data_date = '2013-08-01' and exists( select 1 from sales mth6 where mth6.client_id = a.client_id and mth6.data_date BETWEEN '2013-06-01' and '2013-07-01' ) then case_qty ELSE 0 END) AS M6 ,
sum( case WHEN a.data_date = '2013-09-01' and exists( select 1 from sales mth7 where mth7.client_id = a.client_id and mth7.data_date BETWEEN '2013-07-01' and '2013-08-01' ) then case_qty ELSE 0 END) AS M7 ,
sum( case WHEN a.data_date = '2013-10-01' and exists( select 1 from sales mth8 where mth8.client_id = a.client_id and mth8.data_date BETWEEN '2013-08-01' and '2013-09-01' ) then case_qty ELSE 0 END) AS M8 ,
sum( case WHEN a.data_date = '2013-11-01' and exists( select 1 from sales mth9 where mth9.client_id = a.client_id and mth9.data_date BETWEEN '2013-09-01' and '2013-10-01' ) then case_qty ELSE 0 END) AS M9 ,
sum( case WHEN a.data_date = '2013-12-01' and exists( select 1 from sales mth10 where mth10.client_id = a.client_id and mth10.data_date BETWEEN '2013-10-01' and '2013-12-01' ) then case_qty ELSE 0 END) AS M10 ,
sum( case WHEN a.data_date = '2014-01-01' and exists( select 1 from sales mth11 where mth11.client_id = a.client_id and mth11.data_date BETWEEN '2013-11-01' and '2013-12-01' ) then case_qty ELSE 0 END) AS M11
FROM sales as a
INNER JOIN Products AS P ON P.product_id = a.product_id
WHERE a.client_id IN ('123')
AND a.data_date BETWEEN '2013-03-01' AND '2013-12-01' AND a.case_qty > 0;
Here's a screen shot of the explain
Here's a screen shot of the indexes
change
data_date BETWEEN '2013-01-01' and '2013-02-01'
to
data_date in ('2013-01-01', '2013-01-02',.....,...'2013-02-01' )
but i would turn this whole query into sum cases then use a wrapper to pull out the accounts i need.
From your query it seems like you want the years sales for a product in an entire year. Or something to that affect. So from your data you are almost trying to create a pivot. I had a simlar problem and solved it like this
select
p.product_id,
s.client_id,
sum(case when DATE_FORMAT(a.data_date,'%Y%m') in (201301) then ifnull(case_qty,0) else 0 end) period1,
sum(case when DATE_FORMAT(a.data_date,'%Y%m') in (201301, 201302) then ifnull(case_qty,0) else 0 end) period2,
sum(case when DATE_FORMAT(a.data_date,'%Y%m') in (201301, 201302, 201303) then ifnull(case_qty,0) else 0 end) period3,
sum(case when DATE_FORMAT(a.data_date,'%Y%m') in (201302, 201303, 201304) then ifnull(case_qty,0) else 0 end) period4,
sum(case when DATE_FORMAT(a.data_date,'%Y%m') in (201303, 201304, 201305) then ifnull(case_qty,0) else 0 end) period5,
.
.
.
up to period12
from
sales s
where
a.data_date between '2013-03-01' and '2013-12-31' and
s.client_id = some_value
group by
p.product_id,
s.client_id
Note that the performance of the query has increased drastically since you are only doing a single scan of your sales table (depending on what's in your where clause and indexes). To speed it up you would need indexes on say client_id and data_date for example.
Ideally this query would be run for a report or something where the start and end data is fixed and all the user can change is the year of that date. I wasn't sure if you wanted to group by product_id since in your query you aren't, but you can always remove it to get total per client.
I have adjusted the query based on my understanding in your comments. I am not 100% sure if you can use the keyword 'in' in a case statement. You could alternatively have more case statements.
Related
SELECT SUM(T1.approve_con) AS compl_rec,
SUM(T1.payout) AS payout,
SUM(T1.reversal) AS reversal,
T1.app_id,
T1.end_user,
T1.created
FROM (
(SELECT app_id,end_user,created,
approve_conversions AS `approve_con`,
'0' AS reversal,
payout AS payout
FROM app_end_users
WHERE publisher_id=1625
AND app_id=57)
UNION ALL
(SELECT opinongold_survey_response.app_id,
opinongold_survey_response.user_id AS end_user,
opinongold_survey_response.movement AS created,
(CASE WHEN opinongold_survey_response.survey_status= 'Completed' THEN 1 ELSE 0 END) AS `approve_con`,
(CASE WHEN opinongold_survey_response.survey_status= 'Rejected' THEN 1 ELSE 0 END) AS reversal,
(CASE WHEN opinongold_survey_response.survey_status= 'Completed' THEN payout ELSE 0 END) AS payout
FROM opinongold_survey_response
WHERE pub_id=1625
AND app_id=57)
) AS T1
GROUP BY T1.app_id,T1.end_user
ORDER BY `approve_con` DESC
LIMIT 0, 10
above is my mysql query order by is not working on approve_con, payout, reversal column
please let me where i am doing wrong in this query.
I am trying to optimize my query when utilizing a couple very large data sets. My current query takes a little while to process, even for only a couple days worth of data, whereas this would be intended to pull monthly data.
My question would be what is the best way to pull this off: (I have used one of the datasets in this example, but keep in mind there would be three with basically the same structure, all in the same query)
select u.id, u.name,
count(distinct(case when sales.tag='event' then sales.id end)) as
eventsales,
count(distinct(case when sales.tag='onpremise' then sales.id end))
as onpresales,
count(distinct(case when sales.tag='offpremsales' then sales.id
end)) as offpresales,
count(distinct(case when sales.fulfillment='yes' and
sales.premise='on' then sales.id end)) as fullonsales,
count(distinct(case when sales.fulfillment='no' and
sales.premise='on' then sales.id end)) as fulloffsales
from users u
left join (
select * from sales where org='XXXX' and invoicedate BEWTEEN '2018-
04-01' and '2018-04-10' and status='active'
) sales on sales.user=u.id
where u.status='active'
group by u.org, u.id, u.name
order by u.team
I am no pro, still learning, but is this optimal to perform?
Would it be better to ditch the derived table and utilize 5 subqueries?
Since there are only a couple subtle changes in each, should I create multiple derived tables instead?
Also, my index used in this example would be sales table: org, invoicedate, status
However, from my research, MySQL does not use indexes on derived tables. Is this accurate?
Thanks in advance, let me know if I need to provide any other info.
Entire actual query below
select t.name as team, u.name as "REP NAME",
count(distinct activity.id) as "TOTAL VISITS",
count(distinct activity.account_id) as "UNIQUE VISITS",
ROUND((select sum(s.volumece) from lpmysqldb.sales s where
s.org_id='555b918ae4b07b6ac5050852' and s.account_id IN (select
account_id from lpmysqldb.activity where
org_id='555b918ae4b07b6ac5050852' and user_id=u.id and
(completed_at between '2018-04-01' and '2018-04-04') and
tag='visit' and accountname is not null and (status='active' or
status='true' or status='1')) and (s.invoice_date between
DATE_FORMAT(CURDATE(), '%Y-01-01') and DATE_FORMAT(CURDATE(), '%Y-
%m-%d'))),2) as "CURRENT YEAR VOLUME",
ROUND((select sum(s.volumece) from lpmysqldb.sales s where
s.org_id='555b918ae4b07b6ac5050852' and s.account_id IN (select
account_id from lpmysqldb.activity where
org_id='555b918ae4b07b6ac5050852' and user_id=u.id and
(completed_at between '2018-04-01' and '2018-04-04') and
tag='visit' and accountname is not null and (status='active' or
status='true' or status='1')) and (s.invoice_date between
(DATE_FORMAT(CURDATE(), '%Y-01-01') - INTERVAL 1 YEAR) and
(DATE_FORMAT(CURDATE(), '%Y-%m-%d') - INTERVAL 1 YEAR))),2) as
"PREVIOUS YEAR VOLUME",
count(distinct placement.id) as "COMMITMENTS ADDED",
CASE WHEN
count(distinct activity.account_id) = 0 THEN (count(distinct
placement.id) / 1)
else (cast(count(distinct placement.id) as decimal(10,2)) /
cast(count(distinct activity.account_id) as decimal(10,2)))
END as "UNIQUE VISIT TO COMMITMENT %",
CASE WHEN o.mode='basic' then count(distinct placement.id) else
count(distinct(case when placement.commitmentstatus='fullfilled'
then placement.id end))
END as "COMMITMENTS FULFILLED",
CASE WHEN o.mode='basic' then 1 else
(CASE WHEN
count(distinct placement.id) = 0 THEN (count(distinct(case when
placement.commitmentstatus='fullfilled' then placement.id end)) /
1)
else (cast(count(distinct(case when
placement.commitmentstatus='fullfilled' then placement.id end))
as decimal(10,2)) / cast(count(distinct placement.id) as
decimal(10,2)))
end)
END as "COMMITMENT TO FULFILLMENT %",
CASE WHEN o.mode='basic' then count(distinct placement.id) else
count(distinct(case when placement.commitmentstatus='fullfilled'
AND (premise = 1 or premise IS NULL) then placement.id end))
END as "ON PREM COMMITMENTS FULFILLED",
CASE WHEN o.mode='basic' then count(distinct placement.id) else
count(distinct(case when placement.commitmentstatus='fullfilled'
AND premise = 0 then placement.id end))
END
CASE WHEN o.mode='basic' then count(distinct placement.id) else
count(distinct(case when placement.commitmentstatus='fullfilled'
AND ispackage IN ('1','true','active') then placement.id end))
END as "PACKAGE COMMITMENTS FULFILLED",
CASE WHEN o.mode='basic' then count(distinct placement.id) else
count(distinct(case when placement.commitmentstatus='fullfilled'
AND isdraft IN ('1','true','active') then placement.id end))
END as "DRAFT COMMITMENTS FULFILLED",
(select count(distinct id) from lpmysqldb.activity where
org_id='555b918ae4b07b6ac5050852' and user_id=u.id and
(completed_at between '2018-04-01' and '2018-04-04') and
activity_name IN ('Display','Floor Display') and (activity.status
IN ('1','active','true','') OR activity.status IS NULL)) as
"DISPLAYS BUILT",
(select count(distinct id) from lpmysqldb.activity where
org_id='555b918ae4b07b6ac5050852' and user_id=u.id and
(completed_at between '2018-04-01' and '2018-04-04') and
tag='event' and (activity.status IN ('1','active','true','') OR
activity.status IS NULL)) as "EVENTS"
from lpmysqldb.users u
left join lpmysqldb.teams t on t.team_id=u.team_id
left join lpmysqldb.organizations o on o.id=t.org_id
left join (select * from lpmysqldb.activity where
org_id='555b918ae4b07b6ac5050852' and (completed_at between '2018-
04-01' and '2018-04-04') and tag='visit' and accountname is not
null and (status IN ('1','active','true','') OR status IS NULL))
activity on activity.user_id=u.id
left join (select * from lpmysqldb.placements where
orgid='555b918ae4b07b6ac5050852' and (placementdate between '2018-
04-01' and '2018-04-04') and (status IN ('1','active','true','') OR
status IS NULL)) placement on placement.userid=u.id
where u.org_id='555b918ae4b07b6ac5050852'
and u.status IN ('active','true','1')
and istestuser!='1'
group by u.org_id, t.name, u.id, u.name, o.mode
order by t.name asc, count(distinct activity.id) desc
I need to tabulate the data from three tables for a report. Please see this query
SELECT
DATE( sale.sale_time ) AS dat,
location.location_name as location,
sale.sale_id AS total_orders,
SUM( CASE sale.is_cancelled WHEN 0 THEN sale.sale_amount ELSE 0 END ) AS sales,
SUM( CASE sale.is_cancelled WHEN 0 THEN sale.sale_discount ELSE 0 END ) AS discounts,
SUM( CASE sale.is_cancelled WHEN 0 THEN sale.sale_tax ELSE 0 END ) AS taxes,
COUNT( DISTINCT sale.customer_id ) AS total_customers,
SUM( CASE WHEN DATE( person.added_on ) = DATE( sale.sale_time ) THEN 1 ELSE 0 END ) AS new_customers,
SUM( CASE sale.is_cancelled WHEN 1 THEN 1 ELSE 0 END ) AS cancelled_orders
FROM
sales AS sale
INNER JOIN locations AS location ON location.location_id = sale.location_id
INNER JOIN people AS person ON person.person_id = sale.customer_id
GROUP BY
dat,location
The results for new_customers is showing wrong, often more than total_customers. Where is it wrong, and how to correct this?
The results are like this:
dat location total_orders sales discounts taxes new_customers total_customers cancelled_orders
15-03-14 Location1 52 1355 0 129.04 4 2 0
16-03-14 Location1 56 280 0 30 2 1 0
16-03-14 Location2 59 2518 0 212.2 3 6 2
As you might have guessed from the query, sales table has the columns sale_id,sale_time,sale_cost,sale_discount,sale_tax,sale_amount, is_cancelled (tinyint with values 0 or 1), and customer_id
People table has the columns person_id, first_name, added_on
By comparing the date(salessale_time) to date(person.added_on), I want to list customers added on that date
I modified the query to the following to get new customers also in the result set.
SELECT DATE(sale.sale_time) AS dat, location.location_name as location, (sale.sale_id) AS total_orders,
SUM(CASE sale.is_cancelled WHEN 0 THEN sale.sale_amount ELSE 0 END) AS sales,
SUM(CASE sale.is_cancelled WHEN 0 THEN sale.sale_discount ELSE 0 END) AS discounts,
SUM(CASE sale.is_cancelled WHEN 0 THEN sale.sale_tax ELSE 0 END) AS taxes,
count(distinct(sale.customer_id)) AS total_customers,
(select count(person_id) from people where date(added_on) = date(sale.sale_time) and person_id = sale.customer_id) as new_customers,
SUM(CASE sale.is_cancelled WHEN 1 THEN 1 ELSE 0 END) AS cancelled_orders
FROM sales AS sale
INNER JOIN locations AS location ON location.location_id = sale.location_id
GROUP BY dat,location;
See the SQL query below, it work fine. It calculate the number of Yes, NOT, Other and the number of matching mobile number [Sales field] (D.MobileNo = S.mobile)
SELECT D.Username,
SUM(CASE WHEN D.type = 'Yes' THEN 1 ELSE 0 END) as Yes,
SUM(CASE WHEN D.type = 'Not' THEN 1 ELSE 0 END) as Not,
SUM(CASE WHEN D.type = '' THEN 1 ELSE 0 END) as Other,
SUM(CASE WHEN S.mobile IS NULL THEN 0 ELSE 1 END) as Sales,
COUNT(*) as TOTAL
FROM dairy as D
LEFT JOIN (SELECT DISTINCT mobile FROM sales) as S on D.MobileNo = S.mobile
WHERE source = 'Network'
AND UNIX_TIMESTAMP(CheckDate) >= 1309474800
AND UNIX_TIMESTAMP(CheckDate) <= 1311894000
GROUP BY D.Username
ORDER BY TOTAL DESC
I want to exclude WHERE for the Sales field - it should not be part from CheckDate. Meaning it should check any record in the dairy table without CheckDate for the Sales field.
How can that be done?
If you really want those results in only one query, this might do the trick.
SELECT D.Username,
SUM(CASE WHEN D.type = 'Yes' AND UNIX_TIMESTAMP(CheckDate) >= 1309474800 AND UNIX_TIMESTAMP(CheckDate) <= 1311894000 THEN 1 ELSE 0 END) as Yes,
SUM(CASE WHEN D.type = 'Not' AND UNIX_TIMESTAMP(CheckDate) >= 1309474800 AND UNIX_TIMESTAMP(CheckDate) <= 1311894000 THEN 1 ELSE 0 END) as Not,
SUM(CASE WHEN D.type = '' AND UNIX_TIMESTAMP(CheckDate) >= 1309474800 AND UNIX_TIMESTAMP(CheckDate) <= 1311894000 THEN 1 ELSE 0 END) as Other,
SUM(CASE WHEN S.mobile IS NULL THEN 0 ELSE 1 END) as Sales,
COUNT(*) as TOTAL,
SUM(CASE WHEN UNIX_TIMESTAMP(CheckDate) >= 1309474800 AND UNIX_TIMESTAMP(CheckDate) <= 1311894000 THEN 1 ELSE 0 END) AS TOTALINCHECKDATE
FROM dairy as D
LEFT JOIN (SELECT DISTINCT mobile FROM sales) as S on D.MobileNo = S.mobile
WHERE source = 'Network'
GROUP BY D.Username
ORDER BY TOTAL DESC
Note that "TOTAL" will count all rows (including those who where not within your CheckDate range), TOTALINCHECKDATE return the same value as in your previous query.
Obviously, this can still be optimized.
Assuming username exists also in SALES table
SELECT Username,SUM(Yes) As Yes, SUM(`Not`) As `Not`
, SUM(Other) As Other, SUM(sales) Sales, SUM(total)
FROM (
-- get diary data
SELECT username,mobileNo As mobile,
CASE WHEN D.type = 'Yes' THEN 1 ELSE 0 END as Yes,
CASE WHEN D.type = 'Not' THEN 1 ELSE 0 END as `Not`,
CASE WHEN D.type = '' THEN 1 ELSE 0 END as Other,
0 As sales, 1 as total
FROM dairy as D
WHERE source = 'Network'
AND UNIX_TIMESTAMP(CheckDate) >= 1309474800
AND UNIX_TIMESTAMP(CheckDate) <= 1311894000
UNION ALL
-- get all sales
SELECT DISTINCT username,mobile, 0 as Yes, 0 as `Not`, 0 As Other, 1 As sales, 0 As total
FROM sales
WHERE UNIX_TIMESTAMP(CheckDate) >= 1309474800
AND UNIX_TIMESTAMP(CheckDate) <= 1311894000
) AS a
GROUP BY Username
Also try your original query with date addition
SELECT D.Username,
SUM(CASE WHEN D.type = 'Yes' THEN 1 ELSE 0 END) as Yes,
SUM(CASE WHEN D.type = 'Not' THEN 1 ELSE 0 END) as Not,
SUM(CASE WHEN D.type = '' THEN 1 ELSE 0 END) as Other,
SUM(CASE WHEN S.mobile IS NULL THEN 0 ELSE 1 END) as Sales,
COUNT(*) as TOTAL
FROM dairy as D
LEFT JOIN (SELECT DISTINCT mobile FROM sales WHERE UNIX_TIMESTAMP(CheckDate) >= 1309474800 AND UNIX_TIMESTAMP(CheckDate) <= 1311894000 ) as S on D.MobileNo = S.mobile
WHERE source = 'Network'
AND UNIX_TIMESTAMP(CheckDate) >= 1309474800
AND UNIX_TIMESTAMP(CheckDate) <= 1311894000
GROUP BY D.Username
ORDER BY TOTAL DESC
I am using SUM ( CASE WHEN ) to count number of Yes and No, it work fine.
I am havin problem counting number of matching mobile number from two tables. It dont seem to be counting correctly.
There is a MobileNO field in dairy table and mobile field in the sales table
SELECT
D.Username,
SUM(CASE WHEN D.type = 'Yes' THEN 1 ELSE 0 END) as Yes,
SUM(CASE WHEN D.type = 'No' THEN 1 ELSE 0 END) as No,
SUM(CASE WHEN D.type = '' THEN 1 ELSE 0 END) as Other,
(SELECT SUM(CASE WHEN D.MobileNo = S.mobile THEN 1 ELSE 0 END) from sales as S) as Sales,
COUNT(*) as TOTAL FROM dairy as D
WHERE source = 'Company' AND UNIX_TIMESTAMP(CheckDate) >= 1293840000 AND UNIX_TIMESTAMP(CheckDate) <= 1322697600
group by D.Username order by TOTAL DESC
SELECT
D.Username,
SUM(CASE WHEN D.type = 'Yes' THEN 1 ELSE 0 END) as Yes,
SUM(CASE WHEN D.type = 'No' THEN 1 ELSE 0 END) as No,
SUM(CASE WHEN D.type = '' THEN 1 ELSE 0 END) as Other,
SUM(CASE WHEN S.mobile IS NULL THEN 0 ELSE 1 END) as Sales,
COUNT(*) as TOTAL
FROM dairy as D
LEFT JOIN (SELECT DISTINCT mobile FROM sales) as S ON D.MobileNo = S.mobile
WHERE source = 'Company' AND UNIX_TIMESTAMP(CheckDate) >= 1293840000 AND UNIX_TIMESTAMP(CheckDate) <= 1322697600
group by D.Username order by TOTAL DESC