SQL subquery in query - mysql

I have a table with payment:
worker_id, amount, payed, date
Table workers:
id, name, lname
I need to write SQL that will give me name, lname and sum for jun, july, august, september.
Name | Lname | Sum_JUN | Sum_JULY | Sum_AUG | Sum_SEP
I'm trying with subqueries but can't do it. Can you help me?
I created SQL (example). I will replace dates in PHP.
select w.name, w.lname,
sum(case when p.payed_date between '2014-06-01' and '2014-06-31' then p.amount else 0 end) `sum_june`,
sum(case when p.payed_date between '2014-07-01' and '2014-07-31' then p.amount else 0 end) `sum_july`,
sum(case when p.payed_date between '2014-08-01' and '2014-08-31' then p.amount else 0 end) `sum_august`,
sum(case when p.payed_date between '2014-09-01' and '2014-09-31' then p.amount else 0 end) `sum_september`,
sum(case when p.payed_date between '2014-10-01' and '2014-10-31' then p.amount else 0 end) `sum_november`
from worker w
left join worker_sum p on(w.id = p.worker_id)
group by w.id

You can use conditional aggregation for your desired sum,But this will give you the sum for months from all years exist in your table
select w.*,
sum(case when month(p.date) = 6 then p.amount else 0 end) `sum_june`,
sum(case when month(p.date) = 7 then p.amount else 0 end) `sum_july`,
sum(case when month(p.date) = 8 then p.amount else 0 end) `sum_august`,
sum(case when month(p.date) = 9 then p.amount else 0 end) `sum_september`
from workers w
left join payment p on(w.id = p.worker_id)
group by w.id

Related

SQL JOIN, GROUP BY on Four tables to get Record By Month

I have the following DB design. Tables are:
auth_user
----------
first_name
last_name
staffuser
----------
phone_number
user_id
billing_customerservicebill
-----------------------------
bill_id
service_provider_id
discounted_price
billing_billmanagement
------------------------
creation_date
My query return Sum of discounted_price each user by month row wise. I need every month record show in column.
The following query gives me This record
select a.service_provider_id, first_name, Sum(a.discounted_price), EXTRACT(MONTH FROM c.creation_date)
from billing_customerservicebill a
left outer join users_staffuser b
on a.service_provider_id = b.id
left outer join billing_billmanagement c
on a.bill_id = c.id
left outer join auth_user d
on d.id = b.user_id
where c.creation_date between '2017-11-01' AND '2017-12-31'
group by service_provider_id, first_name, EXTRACT(MONTH FROM c.creation_date)
order by 1
My data show in Table Currently
service_provider_id | first_name | Sum | Month
5 | suneel 31500 | 11
5 | Suneel | 900 | 12
Expected data is
service_provider_id | first_name | Nov | December
5 | suneel | 31500 | 900
The most flexible approach is to use conditional aggregation...
select
a.service_provider_id,
first_name,
SUM(CASE WHEN c.creation_date >= '2017-11-01' AND c.creation_date < '2017-12-01' THEN a.discounted_price END) AS nov,
SUM(CASE WHEN c.creation_date >= '2017-12-01' AND c.creation_date < '2018-01-01' THEN a.discounted_price END) AS dec
from billing_customerservicebill a
left outer join users_staffuser b
on a.service_provider_id = b.id
left outer join billing_billmanagement c
on a.bill_id = c.id
left outer join auth_user d
on d.id = b.user_id
where c.creation_date between '2017-11-01' AND '2017-12-31'
group by service_provider_id, first_name
order by 1
This shows that you need to know in advance which columns you're going to calculate.
Please, try with below solution it's near to your answer:
Where month as column and group by users:
select B.service_provider_id, B.first_name,
(case when month=1 then discounted_price else 0 end) as JAN,
(case when month=2 then discounted_price else 0 end) as FEB,
(case when month=3 then discounted_price else 0 end) as MAR,
(case when month=4 then discounted_price else 0 end) as APR,
(case when month=5 then discounted_price else 0 end) as MAY,
(case when month=6 then discounted_price else 0 end) as JUN,
(case when month=7 then discounted_price else 0 end) as JULY,
(case when month=8 then discounted_price else 0 end) as AUG,
(case when month=9 then discounted_price else 0 end) as SEP,
(case when month=10 then discounted_price else 0 end) as OCT,
(case when month=11 then discounted_price else 0 end) as NOV,
(case when month=12 then discounted_price else 0 end) as DEC
from(
select a.service_provider_id, first_name, Sum(a.discounted_price) as discounted_price, EXTRACT(MONTH FROM c.creation_date) as month
from billing_customerservicebill a
left outer join users_staffuser b
on a.service_provider_id = b.id
left outer join billing_billmanagement c
on a.bill_id = c.id
left outer join auth_user d
on d.id = b.user_id
where c.creation_date between '2017-11-01' AND '2017-12-31'
group by service_provider_id, first_name, EXTRACT(MONTH FROM c.creation_date)
) as B
group by B.service_provider_id, B.first_name

how to sum sql column value into a separate column ? here to sum up NPM,NPF,NPC to a new column

SELECT DATE_FORMAT(e.`date_created`,'%d-%b-%Y') AS DATE,
SUM(CASE WHEN ps.Gender = 'M'
AND e.encounter_type<='6'
AND DATEDIFF(CURDATE(), ps.birthdate)>4381 THEN 1 ELSE 0 END) AS **NPM**,
SUM(CASE WHEN ps.Gender = 'F'
AND e.encounter_type<='6'
AND DATEDIFF(CURDATE(), ps.birthdate)>4381
THEN 1 ELSE 0 END) AS **NPF**,
SUM(CASE WHEN DATEDIFF(CURDATE(), ps.birthdate)<=4380
AND e.encounter_type<='6'
THEN 1 ELSE 0 END) AS **NPC**,
SUM(CASE WHEN ps.Gender = 'M'
AND e.encounter_type>'6'
AND DATEDIFF(CURDATE(), ps.birthdate)>4381 THEN 1 ELSE 0 END) AS OPM,
SUM(CASE WHEN ps.Gender = 'F'
AND e.encounter_type>'6'
AND DATEDIFF(CURDATE(), ps.birthdate)>4381 THEN 1 ELSE 0 END) AS OPF,
SUM(CASE WHEN DATEDIFF(CURDATE(), ps.birthdate)<=4380
AND e.encounter_type>'6' THEN 1 ELSE 0 END) AS OPC,
COUNT(e.`patient_id`) AS total
FROM patient_search ps
LEFT JOIN
(SELECT e.`encounter_id`,
e.`encounter_type`,
e.`patient_id`,
e.`date_created`,
e.`creator`
FROM encounter AS e
GROUP BY e.`creator`,
e.`date_created`) AS e ON e.patient_id=ps.patient_id
LEFT JOIN
(SELECT oo.`concept_id`,
oo.`value_text`,
oo.`encounter_id`
FROM obs AS oo
WHERE oo.`concept_id`='4086')AS eId ON e.`encounter_id`=eId.encounter_id
GROUP BY DATE_FORMAT(e.`date_created`,'%Y-%m-%d')
As you said that your query is working you just need to wrap it as a subquery and sum those three columns as this
Side note: DATE is a reserved word so you need to backtick it.
SELECT `DATE`,
NPM+NPF+NPC AS SUM_TREE_COLS,
OPM,
OPF,
OPC,
total
FROM (
SELECT DATE_FORMAT(e.`date_created`,'%d-%b-%Y') AS `DATE`,
SUM(CASE WHEN ps.Gender = 'M'
AND e.encounter_type<='6'
AND DATEDIFF(CURDATE(), ps.birthdate)>4381 THEN 1 ELSE 0 END) AS NPM,
SUM(CASE WHEN ps.Gender = 'F'
AND e.encounter_type<='6'
AND DATEDIFF(CURDATE(), ps.birthdate)>4381
THEN 1 ELSE 0 END) AS NPF,
SUM(CASE WHEN DATEDIFF(CURDATE(), ps.birthdate)<=4380
AND e.encounter_type<='6'
THEN 1 ELSE 0 END) AS NPC,
SUM(CASE WHEN ps.Gender = 'M'
AND e.encounter_type>'6'
AND DATEDIFF(CURDATE(), ps.birthdate)>4381 THEN 1 ELSE 0 END) AS OPM,
SUM(CASE WHEN ps.Gender = 'F'
AND e.encounter_type>'6'
AND DATEDIFF(CURDATE(), ps.birthdate)>4381 THEN 1 ELSE 0 END) AS OPF,
SUM(CASE WHEN DATEDIFF(CURDATE(), ps.birthdate)<=4380
AND e.encounter_type>'6' THEN 1 ELSE 0 END) AS OPC,
COUNT(e.`patient_id`) AS total
FROM patient_search ps
LEFT JOIN
(SELECT e.`encounter_id`,
e.`encounter_type`,
e.`patient_id`,
e.`date_created`,
e.`creator`
FROM encounter AS e
GROUP BY e.`creator`,
e.`date_created`) AS e ON e.patient_id=ps.patient_id
LEFT JOIN
(SELECT oo.`concept_id`,
oo.`value_text`,
oo.`encounter_id`
FROM obs AS oo
WHERE oo.`concept_id`='4086')AS eId ON e.`encounter_id`=eId.encounter_id
GROUP BY DATE_FORMAT(e.`date_created`,'%Y-%m-%d')
) BIGTAB

MySQL optimize multiple subquery into nested inner join?

I have 3 tables; contracts, dealers, and users.
users have many dealers and dealers have many contracts but the contracts are not directly associated with the users.
I am trying to build a report that gets me a monthly count of completed contracts grouped by user for the last 12 months.
So far I have built a multiple subquery, which is very slow: SQL Fiddle
SELECT *,
( SELECT count(*) FROM contracts
WHERE
dealer_id IN
( SELECT id FROM dealers WHERE user_id = User.id )
AND status = 'Paid'
AND completion_date BETWEEN
'2012-08-01 00:00:00' AND '2012-08-31 23:59:59'
) AS Aug_2012,
( SELECT count(*) FROM contracts
WHERE
dealer_id IN
( SELECT id FROM dealers WHERE user_id = User.id )
AND status = 'Paid'
AND completion_date BETWEEN
'2012-09-01 00:00:00' AND '2012-09-30 23:59:59'
) AS Sep_2012
FROM users AS User
WHERE
id IN( SELECT user_id FROM dealers WHERE active = 1 AND user_id IS NOT NULL GROUP BY user_id )
AND id != 1
ORDER BY User.name ASC
Instead of the subquery which selects each month I'd like to use something like this:
COUNT(*) as last_12_months,
SUM(case when MONTH(completion_date) = 8 then 1 else 0 end) as Aug_2012,
SUM(case when MONTH(completion_date) = 9 then 1 else 0 end) as Sep_2012,
etc.
Since I'd be returning multiple columns I would have to restructure it, but I'm not sure how. If I use an INNER JOIN what clause do I join on?
Here's the final query based on Mikhail's answer below:
SELECT
User.*,
SUM(case when MONTH(completion_date) = 8 then 1 else 0 end) AS Aug_2012,
SUM(case when MONTH(completion_date) = 9 then 1 else 0 end) AS Sep_2012,
SUM(case when MONTH(completion_date) = 10 then 1 else 0 end) AS Oct_2012,
SUM(case when MONTH(completion_date) = 11 then 1 else 0 end) AS Nov_2012,
SUM(case when MONTH(completion_date) = 12 then 1 else 0 end) AS Dec_2012,
SUM(case when MONTH(completion_date) = 1 then 1 else 0 end) AS Jan_2013,
SUM(case when MONTH(completion_date) = 2 then 1 else 0 end) AS Feb_2013,
SUM(case when MONTH(completion_date) = 3 then 1 else 0 end) AS Mar_2013,
SUM(case when MONTH(completion_date) = 4 then 1 else 0 end) AS Apr_2013,
SUM(case when MONTH(completion_date) = 5 then 1 else 0 end) AS May_2013,
SUM(case when MONTH(completion_date) = 6 then 1 else 0 end) AS Jun_2013,
SUM(case when MONTH(completion_date) = 7 then 1 else 0 end) AS Jul_2013,
SUM(case when completion_date BETWEEN '2012-08-01 00:00:00' AND '2013-07-31 23:59:59' then 1 else 0 end) as last_12_months
FROM users AS User
LEFT OUTER JOIN
(
SELECT id, user_id FROM dealers
WHERE active = 1 AND user_id IS NOT NULL
) AS Dealer ON User.id = Dealer.user_id
LEFT OUTER JOIN
(
SELECT completion_date, status, dealer_id FROM contracts
WHERE completion_date BETWEEN '2012-08-01 00:00:00' AND '2013-07-31 23:59:59' AND status = 'Paid' AND cancelled = 0
) AS Contract on Dealer.id = Contract.dealer_id
WHERE
User.id IN
(
SELECT user_id FROM dealers
WHERE active = 1 AND user_id IS NOT NULL
GROUP BY user_id
)
GROUP BY
User.id order by User.name asc
This is about 4 times faster.
Try this:
select
User.id, User.name,
sum(case when MONTH(completion_date) = 8 and Year(completion_date)=2012 then 1 else 0 end) as Aug_2012,
sum(case when MONTH(completion_date) = 9 and Year(completion_date)=2012 then 1 else 0 end) as Sep_2012,
sum(case when MONTH(completion_date) = 10 and Year(completion_date)=2012 then 1 else 0 end) as Oct_2012,
sum(case when MONTH(completion_date) = 11 and Year(completion_date)=2012 then 1 else 0 end) as Nov_2012,
sum(case when MONTH(completion_date) = 12 and Year(completion_date)=2012 then 1 else 0 end) as Dec_2012,
sum(case when MONTH(completion_date) = 1 and Year(completion_date)=2013 then 1 else 0 end) as Jan_2012,
sum(case when MONTH(completion_date) = 2 and Year(completion_date)=2013 then 1 else 0 end) as Feb_2012,
sum(case when MONTH(completion_date) = 3 and Year(completion_date)=2013 then 1 else 0 end) as Mar_2012,
sum(case when MONTH(completion_date) = 4 and Year(completion_date)=2013 then 1 else 0 end) as Apr_2012,
sum(case when MONTH(completion_date) = 5 and Year(completion_date)=2013 then 1 else 0 end) as May_2012,
sum(case when MONTH(completion_date) = 6 and Year(completion_date)=2013 then 1 else 0 end) as Jun_2012,
sum(case when MONTH(completion_date) = 7 and Year(completion_date)=2013 then 1 else 0 end) as Jul_2012
from users AS User
left outer join dealers on
User.id=dealers.user_id
left outer join contracts on
dealers.id=contracts.dealer_id
group by
User.id,
contracts.status
having
contracts.status='Paid'
order by
User.name asc;

SQL query - How to exclude WHERE for specific field

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

Counting number of Sales from two tables

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