How do I combine 2 queries from difference tables - mysql

I have 2 queries. The 1st is to find the netamount and the 2nd is the sum of budget. i need to show the result including the store name and storenumber which are from difference table.
Here is my 1st Query to find total netamount:
SELECT sum(a.netamt) as netamt, b.store_name
FROM site_sales a JOIN site_store b ON b.storenum = a.storenum
WHERE a.busidate >= '2017-01-01' AND a.busidate <='2017-04-30'
GROUP BY a.storenum
The 2nd Query is to find total budget
SELECT
SUM(CASE WHEN c.busidate BETWEEN '2017-01' AND '2017-04' THEN c.budget ELSE 0 END) as budget,
b.store_name
FROM site_kpimthslsbgt c JOIN site_store b ON b.storenum = c.storenum
GROUP BY c.storenum
I need to combine both of this query. The result of output should be
like this

try this Also you need to put up join conditions in the end if there are multiple records fetched in your individual queries :
Select x.netamt, y.budget, y.store_name
from
(
SELECT sum(a.netamt) as netamt, b.store_name
FROM site_sales a JOIN site_store b ON b.storenum = a.storenum
WHERE a.busidate >= '2017-01-01' AND a.busidate <='2017-04-30'
GROUP BY a.storenum
) x
inner join
(
SELECT
SUM(CASE WHEN c.busidate BETWEEN '2017-01' AND '2017-04' THEN c.budget ELSE 0 END) as budget,
b.store_name
FROM site_kpimthslsbgt c JOIN site_store b ON b.storenum = c.storenum
GROUP BY c.storenum
) y
on x.store_name = y.store_name

SELECT sum(a.netamt) as netamt,SUM(CASE WHEN c.busidate BETWEEN '2017-01' AND '2017-04' THEN c.budget ELSE 0 END) as budget,b.store_name
FROM site_store b
JOIN site_kpimthslsbgt c ON b.storenum = c.storenum
JOIN site_sales a ON b.storenum = a.storenum
GROUP BY b.storenum
Try above query.
Hope this will help you.

I think there are 4 possible scenarios here, stores which have sales and budget, stores which have sales but no budget, stores which have budget but no sales, stores which have neither sales nor budget.
Given
DROP TABLE IF EXISTS site_sales,site_budget;
CREATE TABLE site_sales(ID INT auto_increment primary key, site_id int, busidate date,amt int);
create table site_budget(ID INT auto_increment primary key, site_id int, busidate date,amt int);
insert into site_sales (site_id,busidate,amt) values
(1,'2017-04-01',10),(1,'2017-04-01',20),
(2,'2017-04-01',10);
insert into site_budget (site_id,busidate,amt) values
(1,'2017-04-01',200),
(3,'2017-04-01',100);
This query
SELECT b.name,
sum(case when a.busidate between '2017-01-01' and '2017-04-30' then a.amt else 0 end) as netamt,
ifnull((select SUM(CASE WHEN c.busidate BETWEEN '2017-01-01' AND '2017-04-30' THEN c.amt ELSE 0 END) from site_budget c where b.id = c.site_id ),0) as budget
FROM sites b
left JOIN site_sales a ON b.id = a.site_id
GROUP BY b.id
result
+--------+--------+--------+
| name | netamt | budget |
+--------+--------+--------+
| Store1 | 30 | 200 |
| Store2 | 10 | 0 |
| Store3 | 0 | 100 |
| Store4 | 0 | 0 |
+--------+--------+--------+
4 rows in set (0.00 sec)

Related

Unified a data based on dates

I Have this query
SELECT DATE_FORMAT(bookings.start_at, '%m/%d/%Y') as "Date",
CASE WHEN payment.type = "CASH" THEN sum(payment.amount) end as "Cash" ,
CASE WHEN payment.type = "Credit" THEN sum(payment.amount) end as "Credit"
from orders
inner join bookings on bookings.id = orders.booking_id
inner join payment on payment.order_id = orders.id
where (bookings.start_at BETWEEN '2022-03-09' AND '2022-03-09 23:59:00')
group by payment.type;
And it returning like this
Date | Cash | Credit
2022/03/09 | NULL | NULL
2022/03/09 | 2000 | NULL
2022/03/09 | NULL | 5000
What i want to achieve is like this
Date | Cash | Credit
2022/03/09 | 2000 | 5000
i already tried GROUP BY for start_at but it return a different kind of time but same date
SELECT DATE_FORMAT(bookings.start_at, '%m/%d/%Y') as `Date`,
SUM(CASE WHEN payment.type = "CASH"
THEN payment.amount
ELSE 0
END) as `Cash`,
SUM(CASE WHEN payment.type = "Credit"
THEN payment.amount
ELSE 0
END) as `Credit`
from orders
inner join bookings on bookings.id = orders.booking_id
inner join payment on payment.order_id = orders.id
where bookings.start_at BETWEEN '2022-03-09' AND '2022-03-09 23:59:00'
group by 1;

Calculate difference between min and max for each column only if higher then 0

I need to calculate the difference between odds based on the value in the 'updated' column at the moment I take odds where the updated value is a min and minus it from odds where the updated value is max. It works perfect but I've just realized that in some columns happens to be 0 sometimes and I was wondering if it's possible to select the minimum still based on the updated column and only values where higher than 0.
That's how the table looks like
fixture_id
H_odds
D_odds
A_odds
ev_tstamp
updated
120000
1.40
1.50
1.30
132000
12
120000
1.10
1.10
1.10
132000
11
120000
1.20
0
1.60
132000
10
And that's what I would like to get back
fixture_id
H_odds
D_odds
A_odds
ev_tstamp
updated
dif_h
dif_d
dif_a
120000
1.40
1.50
1.30
132000
12
0.2
0.4
-0.3
That's what I'm getting back at the moment
fixture_id
H_odds
D_odds
A_odds
ev_tstamp
updated
dif_h
dif_d
dif_a
120000
1.40
1.50
1.30
132000
12
0.2
1.5
-0.3
The code I'm using
select
t_max.*,
(t_max.H_odds - t_min.H_odds) as dif_h,
(t_max.D_odds - t_min.D_odds) as dif_d,
(t_max.A_odds - t_min.A_odds) as dif_a
from
(
select
fixture_id,
min(updated) min_updated,
max(updated) max_updated
from
test
group by
fixture_id
) as t1
join test as t_min on (t_min.fixture_id = t1.fixture_id and t_min.updated = t1.min_updated)
join test as t_max on (t_max.fixture_id = t1.fixture_id and t_max.updated = t1.max_updated)
Consider the following:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(fixture_id INT NOT NULL
,updated INT NOT NULL
,outcome ENUM('Home win','Draw','Away win') NOT NULL
,odds DECIMAL(5,2) NOT NULL
,PRIMARY KEY(fixture_id,outcome,updated)
);
INSERT INTO my_table VALUES
(120,12,'Home win',1.40),
(120,11,'Home win',1.10),
(120,10,'Home win',1.20),
(120,12,'Draw',1.50),
(120,11,'Draw',1.10),
(120,12,'Away win',1.30),
(120,11,'Away win',1.10),
(120,10,'Away win',1.60);
Latest odds:
SELECT x.*
FROM my_table x
JOIN
( SELECT fixture_id
, outcome
, MAX(updated) min_updated
FROM my_table x
GROUP
BY fixture_id
, outcome
) y
ON y.fixture_id = x.fixture_id
AND y.outcome = x.outcome
AND y.min_updated = x.updated;
Earliest odds:
Earliest odds:
SELECT x.*
FROM my_table x
JOIN
( SELECT fixture_id
, outcome
, MIN(updated) min_updated
FROM my_table x
GROUP
BY fixture_id
, outcome
) y
ON y.fixture_id = x.fixture_id
AND y.outcome = x.outcome
AND y.min_updated = x.updated;
Delta:
SELECT a.*
, a.odds - b.odds delta
FROM
( SELECT x.*
FROM my_table x
JOIN
( SELECT fixture_id
, outcome
, MAX(updated) min_updated
FROM my_table x
GROUP
BY fixture_id
, outcome
) y
ON y.fixture_id = x.fixture_id
AND y.outcome = x.outcome
AND y.min_updated = x.updated
) a
JOIN
( SELECT x.*
FROM my_table x
JOIN
( SELECT fixture_id
, outcome
, MIN(updated) min_updated
FROM my_table x
GROUP
BY fixture_id
, outcome
) y
ON y.fixture_id = x.fixture_id
AND y.outcome = x.outcome
AND y.min_updated = x.updated
) b
ON b.fixture_id = a.fixture_id
AND b.outcome = a.outcome;
Result:
+------------+---------+----------+------+-------+
| fixture_id | updated | outcome | odds | delta |
+------------+---------+----------+------+-------+
| 120 | 12 | Home win | 1.40 | 0.20 |
| 120 | 12 | Draw | 1.50 | 0.40 |
| 120 | 12 | Away win | 1.30 | -0.30 |
+------------+---------+----------+------+-------+
This solution only works on MySQL 8+.
I would suggest window functions. The following treats each odds column separately . . . and it does not make any assumptions about the odds increasing or decreasing with each update:
select fixture_id, ev_tstamp, max(updated),
max(case when update = max_h_update then h_odds end) as max_h,
max(case when update = max_d_update then h_odds end) as max_d,
max(case when update = max_a_update then h_odds end) as max_a,
(max(case when update = max_h_update then h_odds end) -
max(case when update = min_h_update then h_odds end)
) as h_diff,
(max(case when update = max_d_update then d_odds end) -
max(case when update = min_d_update then d_odds end)
) as d_diff,
(max(case when update = max_a_update then a_odds end) -
max(case when update = min_a_update then a_odds end)
) as a_diff
from (select t.*,
max(case when h_odds <> 0 then update end) over (partition by fixture_id) as max_h_update,
min(case when h_odds <> 0 then update end) over (partition by fixture_id) as min_h_update,
max(case when d_odds <> 0 then update end) over (partition by fixture_id) as max_h_update,
min(case when d_odds <> 0 then update end) over (partition by fixture_id) as min_h_update,
max(case when a_odds <> 0 then update end) over (partition by fixture_id) as max_a_update,
min(case when a_odds <> 0 then update end) over (partition by fixture_id) as min_a_update
from test t
) t
group by fixture_id, ev_tstamp;
I just modify the code little bit to calculate the difference only for specific group of odds (avg) so it look like bellow. It worked just once though, it took over 15 seconds to process and the other times I tried it didn't work due to time out error. Just to clarify in my structure the market column is the 'outcome' column from your example.
explain SELECT a.*
, a.odds - b.odds delta
FROM
( SELECT x.*
FROM average_odds x
JOIN
( SELECT fix_id
, market
, MAX(updated) min_updated
FROM average_odds x where odds_type=avg
GROUP BY fix_id
, market
) y
ON y.fix_id = x.fix_id
AND y.market = x.market
AND y.min_updated = x.updated
) a
JOIN
( SELECT x.*
FROM average_odds x
JOIN
( SELECT fix_id
, market
, MIN(updated) min_updated
FROM average_odds x where odds_type=avg
GROUP BY fix_id
, market
) y
ON y.fix_id = x.fix_id
AND y.market = x.market
AND y.min_updated = x.updated
) b
ON b.fix_id = a.fix_id
AND b.market = a.market
ORDER BY `delta` ASC
That's the explain table
ID
S TYPE
table..
parti
type
pos_keys
KEY
key len
ref
rows
filtered
extra
1
PRIMARY
derived3>
null
all
null
null
null
null
17466
100.00
Using temporary; Using filesort
1
PRIMARY
x
null
ref
fix,fixi,market,updat
fix
4
y.fix_id
596
0.11
Using where
1
PRIMARY
x
null
ref
fix,fixi,market,updat
fix
4
y.fix_id
596
2.27
Using where
1
PRIMARY
derived5>
null
ref
auto_key0>
auto_key0>
31
y.fix_id,y.market,bobi.x.updated
10
100.00
using index
5
DERIVED
x
null
ref
boki
boki
4
const
17466
100.00
Using index condition; Using temporary; Using file...
3
DERIVED
x
null
ref
boki
boki
4
const
17466
100.00
Using index condition; Using temporary; Using file...

Alternatives to joins in mysql

I have a query as follows that retrieves the status for different stores in a table and displays it as different columns.
SELECT a.Store_ID,b.total as order_completed,c.total as order_cancelled,d.total as order_processed,e.total as order_failed FROM ORDER_HISTORY a
-> LEFT OUTER JOIN(select Store_ID,count(*) as total from ORDER_HISTORY where Status = 57 group by Store_ID)b on a.Store_ID = b.Store_ID
-> LEFT OUTER JOIN(select Store_ID,count(*) as total from ORDER_HISTORY where Status = 53 group by Store_ID)c on a.Store_ID = c.Store_ID
-> LEFT OUTER JOIN(select Store_ID,count(*) as total from ORDER_HISTORY where Status = 52 group by Store_ID)d on a.Store_ID = d.Store_ID
-> LEFT OUTER JOIN(select Store_ID,count(*) as total from ORDER_HISTORY where Status = 62 group by Store_ID)e on a.Store_ID = e.Store_ID
-> group by a.Store_ID;
Can anybody suggest an alternative to using joins as it affects the performance of db operations.
Create an index on ORDER_HISTORY over (Store_ID, Status), then this should be plenty fast.
SELECT
Store_ID,
status,
COUNT(*) as total
FROM
ORDER_HISTORY
GROUP BY
Store_ID,
status;
Then use your application to display the few resulting rows data in columns. Should not be hard to implement.
Another approach would be (same index as above):
SELECT
Store_ID,
SUM(CASE WHEN Status = 57 THEN 1 ELSE 0 END) AS order_completed,
SUM(CASE WHEN Status = 53 THEN 1 ELSE 0 END) AS order_cancelled,
SUM(CASE WHEN Status = 52 THEN 1 ELSE 0 END) AS order_processed,
SUM(CASE WHEN Status = 62 THEN 1 ELSE 0 END) AS order_processed
FROM
ORDER_HISTORY
GROUP BY
Store_ID;
Replace NULL values as appropriate.
Try using a trigger. Same as a stored procedure that executes when an event occurs within the database. maybe it will help you.

CASE statement in SQL giving not proper values

I am having abnormal values when I run this part in my sql code. SQL syntax wise, everything is okay with this?
select
COUNT(CASE WHEN bt.idBillingStatus = 2
THEN 1
ELSE NULL END) AS successfulbillinghits,
SUM(CASE WHEN bt.idBillingStatus = 2
THEN price
ELSE 0.0 END)
AS old_revenue
from table
Overall Query is this. The result of successfulbillinghits should be equal to timesbilled
SELECT
cs.idCustomerSubscription,
cs.msisdn,
pro.name AS promoterName,
c.name AS ClubName,
c.idClub AS ClubID,
o.name AS operatorName,
o.idOperator AS OperatorID,
co.name AS country,
-- cu.customerSince AS CustomerSince,
cs.subscribeddate AS subscribeddate,
-- cs.subscriptionNotificationSent AS SubNotificationSent,
-- cs.eventId AS EventId,
cs.unsubscribeddate AS unsubscribeddate,
cs.firstBillingDate AS FirstBillingDate,
cs.lastBilledDate As LastBilledDate,
cs.lastAttemptDate AS LastAttemptDate,
-- smp.code AS packageName,
-- o.mfactor AS mmfactor,
-- cs.idSubscriptionSource AS SubscriptionChannel,
-- cs.idUnsubscribeSource AS UnsubscriptionChannel,
-- DATE(bt.creationDate) AS BillingCreationDate,
-- bt.price AS pricePerBilling,
-- cs.lastRetryDate As LastRetryDate,
-- cs.lastRenewalDate AS LastRenewalDate,
-- cs.isActive AS ActiveStatus,
-- COUNT(bt.idBillingTransaction) AS BillingAttempts,
curr.idcurreny_symbol AS CurrencyID,
curr.symbol AS currency,
date(bt.creationDate) AS BillingDate,
cs.lastBilledAmount As LastBilledAmount,
cs.timesbilled,
price,
-- sum(price),
-- revenueShareAmountLocal,
-- o.mfactor,
-- count(IFF (bt.idBillingStatus = 2,1,0)) as otherversion,
count(CASE WHEN bt.idBillingStatus = 2
THEN 1
ELSE 0 END) AS successfulbillinghits,
SUM(CASE WHEN bt.idBillingStatus = 2
THEN price
ELSE 0.0 END)
AS old_revenue
FROM
customersubscription cs
LEFT JOIN
billing_transaction bt
ON CONVERT(cs.msisdn USING latin1) = bt.msisdn
AND cs.idClub = bt.idClub
AND bt.creationDate BETWEEN cs.SubscribedDate AND COALESCE(cs.UnsubscribedDate, now())
INNER JOIN customer cu ON (cs.idCustomer = cu.idcustomer)
INNER JOIN operator o ON (o.idoperator = cu.idoperator)
INNER JOIN country co ON (co.`idCountry` = o.idCountry)
INNER JOIN curreny_symbol curr ON (curr.idcurreny_symbol = co.idCurrencySymbol)
LEFT JOIN Promoter pro ON cs.idPromoter = pro.id
INNER JOIN club_operator_relationships cor ON cor.clubId = cs.idClub
INNER JOIN club c ON c.idClub = cs.idClub
-- INNER JOIN operator op ON op.idOperator = cu.idOperator
WHERE
-- (cs.timesbilled > 0 and cs.subscribeddate < '2016-09-01 00:00:00' )
cs.subscribeddate between '2017-04-20 00:00:00' and '2017-04-21 00:00:00'
AND cs.idClub IN (39)
GROUP BY idCustomerSubscription, ClubName, operatorName, promoterName
Successfulbillinghits is much greater than timesbilled in the result
Instead of COUNTuse SUM, as count counts blanks or nulls also
select
SUM(CASE WHEN bt.idBillingStatus = 2
THEN 1
ELSE 0 END) AS successfulbillinghits,
SUM(CASE WHEN bt.idBillingStatus = 2
THEN price
ELSE 0.0 END)
AS old_revenue
from table
Instead of using CASE, you can use WHERE clause with these aggregate functions, e.g.:
SELECT COUNT(*) as `successfulbillinghits`, SUM(price) as `old_revenue`
FROM table bt
WHERE bt.idBillingStatus = 2;

Join between Tables in SQL

I have 5 tables
1. SCHOOL[id(bigInt, primary), name(varchar)]
2. SELECTED_INDICATOR[id(bigInt, primary), school_id(bigint)]
3. TEACHER[id(bigint, primary), indicator_id(bigInt), attendance_id(int)]
4. STUDENT[id(bigint, primary), indicator_id(bigInt), attendance_id(int)]
5. MIDDAY_MEAL[id(bigint,primary), indicator_id(bigint), served(boolean), consumed_number(int)]
in TEACHER table, attendance_id can have value: 1 or 2 or 3.
Similarly, in STUDENT table, attendance_id can have value: 1 or 2.
I have to generate a report based on the SELECTED_INDICATOR id, in a format as:
School_id | School_Name | Total_Teacher | Teacher_1 | Teacher_2 | Teacher_3 | Total_Student | Student_1 | Student_2 | served | consumed_number
for this I have tried as:
select A.id, A.school_id, SC.name,
SUM(CASE WHEN T.attendance_id IN (1,2,3) THEN 1 ELSE 0 END) as TOTAL_TEACHER,
SUM(CASE WHEN T.attendance_id IN (1) THEN 1 ELSE 0 END) as TEACHERS_1,
SUM(CASE WHEN T.attendance_id IN (2) THEN 1 ELSE 0 END) as TEACHERS_2,
SUM(CASE WHEN T.attendance_id IN (3) THEN 1 ELSE 0 END) as TEACHERS_3,
SUM(CASE WHEN S.attendance_id IN (1,2) THEN 1 ELSE 0 END) as TOTAL_STUDENT,
SUM(CASE WHEN S.attendance_id IN (1) THEN 1 ELSE 0 END) as STUDENTS_1,
SUM(CASE WHEN S.attendance_id IN (2) THEN 1 ELSE 0 END) as STUDENTS_2,
M.served, M.consumed_number
from SELECTED_INDICATOR A
join SCHOOL SC on A.school_id = SC.id
join TEACHER T on A.id = T.indicator_id
join STUDENT S on A.id = S.indicator_id
join MIDDAY_MEAL M on A.id = M.indicator_id
WHERE A.STATUS = 'COMPLETED' group by A.id;
When I join TEACHER or STUDENT with SELECTED_INDICATOR one at a time, it gives me the correct data. But when I join both the TEACHER and STUDENT with SELECTED_INDICATOR as in the above query, I get huge numbers for teacher and student related fields.
what is wrong with my query? Please help to correct it, or give any alternative query.
Try using COUNT() that have the options to use distincted values. The problem is that the tables are multiplying the results.
select A.id, A.school_id, SC.name,
COUNT(DISTINCT CASE WHEN T.attendance_id IN (1,2,3) THEN t.TeacherID END) as TOTAL_TEACHER,
COUNT(DISTINCT CASE WHEN T.attendance_id IN (1) THEN t.TeacherID END) as TEACHERS_1,
....
FROM ....
SELECT A.id, A.school_id, SC.name,
(SELECT COUNT(T.attendance_id) FROM TEACHER T GROUP BY T.indicator_id HAVING A.id = T.indicator_id) AS TOTAL_TEACHER,
(SELECT COUNT(T.attendance_id) FROM TEACHER T WHERE T.attendance_id = 1 GROUP BY T.indicator_id HAVING A.id = T.indicator_id) AS TEACHERS_1,
(SELECT COUNT(T.attendance_id) FROM TEACHER T WHERE T.attendance_id = 2 GROUP BY T.indicator_id HAVING A.id = T.indicator_id) AS TEACHERS_2,
(SELECT COUNT(T.attendance_id) FROM TEACHER T WHERE T.attendance_id = 3 GROUP BY T.indicator_id HAVING A.id = T.indicator_id) AS TEACHERS_3,
(SELECT COUNT(S.attendance_id) FROM STUDENT S GROUP BY S.indicator_id HAVING A.id = S.indicator_id) AS TOTAL_STUDENT,
(SELECT COUNT(S.attendance_id) FROM STUDENT S WHERE S.attendance_id = 1 GROUP BY S.indicator_id HAVING A.id = S.indicator_id) AS STUDENTS_1,
(SELECT COUNT(S.attendance_id) FROM STUDENT S WHERE S.attendance_id = 2 GROUP BY S.indicator_id HAVING A.id = S.indicator_id) AS STUDENTS_2,
M.served, M.consumed_number
FROM SELECTED_INDICATOR A
JOIN SCHOOL SC on A.school_id = SC.id
JOIN MIDDAY_MEAL M on A.id = M.indicator_id