mysql - add row wise different incentive - mysql

I am executing one query which is giving me output as I want. I am stuck at the last step of the solution.
Query
SELECT sum(`purchase`.`purchase_ammount`) as Purchase, `franchise`.`district_id`
FROM `purchase`
LEFT JOIN `franchise`
ON `purchase`.`f_id` = `franchise`.`id`
WHERE `franchise`.`district_id` in (
select `district`.`id`
from `district`
LEFT JOIN `region`
ON `district`.`region_id` = `region`.`id`
LEFT JOIN `regional_owner`
ON `regional_owner`.`r_id` = `region`.`id`
WHERE `regional_owner`.`email_id` = 'balajibarhate10#gmail.com'
)
AND purchase.purchase_date
BETWEEN Date_format(Curdate(), "%Y-%m-01") AND Curdate()
GROUP BY `franchise`.`district_id`
Output
+----------+-------------+
| Purchase | district_id |
+----------+-------------+
| 18151 | 4 |
| 24603 | 6 |
+----------+-------------+
What I want
I would like to add 5% in Purchase of the first row if district_id is 4 and 2% in Purchase of the second row if district_id is not 4. I want it in the query itself. How can I achieve this?

This should work
SELECT (CASE WHEN district_id = 4 THEN Purchase * 1.05
ELSE Purchase * 1.02 END) PURCHASE_AMT, district_id
FROM
(
SELECT sum(`purchase`.`purchase_ammount`) as Purchase, `franchise`.`district_id`
FROM `purchase`
LEFT JOIN `franchise`
ON `purchase`.`f_id` = `franchise`.`id`
WHERE `franchise`.`district_id` in (
select `district`.`id`
from `district`
LEFT JOIN `region`
ON `district`.`region_id` = `region`.`id`
LEFT JOIN `regional_owner`
ON `regional_owner`.`r_id` = `region`.`id`
WHERE `regional_owner`.`email_id` = 'balajibarhate10#gmail.com'
)
AND purchase.purchase_date
BETWEEN Date_format(Curdate(), "%Y-%m-01") AND Curdate()
GROUP BY `franchise`.`district_id`
) A

You can use a case statement.
CASE
WHEN district_id = 4 THEN purchase_amount * 1.05
ELSE purchase_amount * 1.02
END AS AdjustedPrice
Of course, you can adjust the assigned name of the calculated field as need be.

Related

ID associated with max date in group

there are a ton of other related questions but I couldn't quite apply them to my situation. I am using sequelize so I'm trying to do this with as few hits to the DB as possible.
My (simplified) table is basically:
id dateAcquired staffId skillId
44 2017-04-27 25 2
341 2018-02-01 28 2
4787 2018-04-04 25 2
8852 2020-01-31 28 2
I am looking for just the id of the most recent dateAcquired per staffId. (note that the most recent one might be a lower id, otherwise I would have had a solution)
4787
8852
Just in SQL using this query I get the correct date but not the correct id:
SELECT id, staffId, max(`dateAcquired`)
FROM `histories` AS `history`
WHERE `history`.`skillId` = '2'
GROUP BY `staffId`, id
Results in:
44 2018-04-04
341 2020-01-31
Although I know it needs tweaking once I get the query right, my sequelize code is:
models.history.findAll(
{
attributes: [sequelize.fn("max", sequelize.col('dateAcquired')), 'id'],
where: {skillId:id},
group: ["id"]
}
).then(maxIds => {
let ids = maxIds.map(result =>{return result.id;});
models.history.findAll({
include: [
{
model:models.staff
}
],
where: {
id: {
[Op.in]: [ids]
}
}
}).then(results =>
{
res.json(results);
})
})
Thanks for your help!
With NOT EXISTS:
select h.* from histories h
where h.skillid = 2
and not exists (
select 1 from histories
where skillid = h.skillid and staffid = h.staffid and dateAcquired > h.dateAcquired
)
See the demo.
Results:
| id | dateAcquired | staffId | skillId |
| ---- | ------------------- | ------- | ------- |
| 4787 | 2018-04-04 00:00:00 | 25 | 2 |
| 8852 | 2020-01-31 00:00:00 | 28 | 2 |
you can try
Create table #test
(id int,
dateAcquired date,
staffid int,
skillid int
)
Insert into #test values (44 , '2017-04-27' , 25 , 2)
Insert into #test values (341 , '2018-02-01' , 28 , 2)
Insert into #test values (4787 , '2018-04-04' , 25 , 2)
Insert into #test values (8852 , '2020-01-31' , 28 , 2)
select id,dateacquired
from
(
select id,dateacquired,
--ROW_NUMBER()over(partition by dateacquired order by dateacquired )
ROW_NUMBER()over( order by dateacquired desc )rn
from #test
)a where rn<=2
Query
SELECT t.*
FROM my_table t
LEFT JOIN my_table t2 ON t2.staffId = t.staffId AND t2.dateAcquired > t.dateAcquired
WHERE t2.id IS NULL
AND t.skillId = 2;
Explanation
What happens is that each row from t joins onto any rows where the staffId matches and dateAcquired is greater. The only rows that don't join are the ones with the highest values in dateAcquired. We then filter out everything that does join in the WHERE clause.
Several possible query patterns to return the specified result
Using an inline view (derived table) to get the latest (maximum) date_acquired for each staffid, and then a join to the base table to get the row(s) that have that latest date_acquired.
SELECT MAX(h.`id`) AS `id`
, h.`staffId`
, h.`dateacquired`
FROM
JOIN ( SELECT l.`staffid`
, MAX(l.`dateacquired`) AS `max_dateacquired`
FROM `histories` l
WHERE l.`skillId` = '2'
GROUP BY l.`staffid`
) m
JOIN `histories` h
ON h.`dateacquired` = m.`max_dateacquired`
ON h.`staffid` = m.`staffid`
AND h.`skillId` = '2'
GROUP
BY h.`staffid`
, h.`dateacquired`
MySQL 8.0 introduced Window Functions, which gives us another way to get the specified result:
WITH w AS
( SELECT h.id
, h.staffid
, h.dateacquired
, ROW_NUMBER() OVER(PARTITION BY h.staffid ORDER BY h.dateacquired DESC, h.id DESC) AS _rn
FROM `histories` h
WHERE h.skillid = '2'
)
SELECT w.id
, w.staffid
, w.dateacquired
FROM w
WHERE _rn = 1
ORDER
BY w.staffid
We could also use an anti-join pattern, to retrieve rows where there isn't a later dateacquired... assuming id is unique in histories (or at least the (staffid,dateacquired,id) tuple is unique)
SELECT h.id
, h.staffid
, h.dateacquired
FROM `histories` h
-- anti-join
LEFT
JOIN `histories` l
ON l.skillid = '2'
AND l.staffid = h.staffid
AND l.dateacquired >= h.dateacquired
AND ( l.datecquired > h.dateacquired OR l.id > h.id )
WHERE l.staffid IS NULL
AND h.skillid = '2'
ORDER
BY h.staffid
We could accomplish the same thing, re-writing the anti-join as a NOT EXISTS
SELECT h.id
, h.staffid
, h.dateacquired
FROM `histories` h
WHERE h.skillid = '2'
AND NOT EXISTS
( SELECT 1
FROM `histories` l
WHERE l.skillid = '2'
AND l.staffid = h.staffid
AND l.dateacquired >= h.dateacquired
AND ( l.datecquired > h.dateacquired OR l.id > h.id )
)
(Note that some of these queries could be simplified a tiny bit if we have a guarantee that (staffid,skillid,dateacquired) tuple is unique. All of the queries above do not assume such a guarantee.)

Counting between dates

I need the count of all dates including the nonexistent
SELECT ifnull(COUNT(*),0) as num , date_format(c.dataCupo,"%d/%m/%Y") as data
FROM cupons c
WHERE c.dataCupo between "2017-02-02" AND "2018-05-04" AND c.proveidor!="VINCULADO" and c.empresa=1
group by date_format(c.dataCupo,"%Y-%m-%d")
//And I need to count all months including the nonexistent
SELECT ifnull(COUNT(*),0) as num , date_format(c.dataCupo,"%m/%Y") as data
FROM cupons c
WHERE c.dataCupo between "2017-02-02" AND "2018-05-04" AND c.proveidor!="VINCULADO" and c.empresa=1
group by date_format(c.dataCupo,"%Y-%m")
//And I need to count of all years including the nonexistent
SELECT ifnull(COUNT(*),0) as num , date_format(c.dataCupo,"%Y") as data
FROM cupons c
WHERE c.dataCupo between "2015-02-02" AND "2018-05-04" AND c.proveidor!="VINCULADO" and c.empresa=1
group by date_format(c.dataCupo,"%Y")
The result i want its:
02/02/2017 | 10
03/02/2017 | 0
04/02/2017 | 2
05/02/2017 | 0
....
AND
02/2017 | 50
03/2017 | 0
04/2017 | 10
AND
2015 | 0
2016 | 10
2017 | 15
2018 | 0
Easiest way to do this is with a Calendar table. This table will have a datetime column that you can join to and is really useful for reporting. Here goes an example of how to make one in MySQL.
https://gist.github.com/bryhal/4129042
Now that you have the Calendar table, you can join to it to find counts of all dates in a date range.
All days example:
select num, td.db_date
FROM
time_dimension td
left join
(SELECT ifnull(COUNT(*),0) as num , c.dataCupo as data
FROM cupons c
WHERE c.dataCupo between "2017-02-02" AND "2018-05-04" AND
c.proveidor!="VINCULADO" and c.empresa=1
group by c.dataCupo) t
on t.data = td.db_date
WHERE td.db_date between "2017-02-02" AND "2018-05-04"
All months example:
select
sum(t.num),
CONCAT(month(td.db_date),"-",year(td.db_date))
FROM
time_dimension td
left join
(SELECT
ifnull(COUNT(*),0) as num ,
c.dataCupo as data
FROM cupons c
WHERE c.dataCupo between "2017-02-02" AND "2018-05-04" AND
c.proveidor!="VINCULADO" and c.empresa=1) t
on c.data = t.data
WHERE td.db_date between "2017-02-02" AND "2018-05-04"
group by CONCAT(month(td.db_date),"-",year(td.db_date))
You should create A Temporary Table To Store All The Date Ranges Between Your Date Ranges
CREATE TEMPORARY TABLE IF NOT EXISTS AllDateRange engine=memory
SELECT DATE(cal.date) Date
FROM (
SELECT ( case when #prmToDate = #prmFromDate then #prmFromDate else
SUBDATE( #prmFromDate, INTERVAL (DATEDIFF(#prmToDate,#prmFromDate)) DAY) + INTERVAL xc DAY end ) AS Date
FROM (
SELECT #xi:=#xi+1 as xc from
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc1,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc2,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc3,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc4,
(SELECT #xi:=+1) xc0
) xxc1
) cal WHERE DATE( cal.date) >= DATE(#prmFromDate) and DATE( cal.date) <= DATE(#prmToDate) ;
And Then Join It With Your Table As.
SELECT count(COALESCE(c.empresa, 0)) as num , date_format(a.Date,"%d/%m/%Y") as data from AllDateTimeRange a
left join cupons c on a.Date=date_format(c.dataCupo,"%Y-%m-%d")
WHERE c.dataCupo between #prmFromDate AND #prmToDate AND c.proveidor!="VINCULADO" and c.empresa=1
group by date_format(c.dataCupo,"%Y-%m-%d");
Similarly Create Temp Tables For Month & Year And Then Join With Your Primary Table, as above in order to get your required results for month and year respectively.

SQL query joining a few tables (MySQL)

I need a "little" help with an SQL query (MySQL).
I have the following tables:
COURIERS table:
+------------+
| COURIER_ID |
+------------+
DELIVERIES table:
+-------------+------------+------------+
| DELIVERY_ID | COURIER_ID | START_DATE |
+-------------+------------+------------+
ORDERS table:
+----------+-------------+-------------+
| ORDER_ID | DELIVERY_ID | FINISH_DATE |
+----------+-------------+-------------+
COORDINATES table:
+-------------+-----+-----+------+
| DELIVERY_ID | LAT | LNG | DATE |
+-------------+-----+-----+------+
In the real database I have more columns in each table, but for this example the above columns are enough.
What do I need?
An SQL query that returns all couriers [COURIER_ID], their last
delivery [DELIVERY_ID] (based on last START_DATE), the
delivery's last coordinate [LAT and LNG] (based on last DATE) and the remaining orders count (total of orders of the last delivery that have no FINISH_DATE).
A courier can have no deliveries, in this case I want DELIVERY_ID =
NULL, LAT = NULL and LNG = NULL in the result.
A delivery can have no coordinates, in this case I want LAT = NULL
and LNG = NULL in the result.
What was I able to do?
SELECT c.`COURIER_ID`,
d.`DELIVERY_ID`,
r.`LAT`,
r.`LNG`,
(SELECT COUNT(DISTINCT `ORDER_ID`)
FROM `ORDERS`
WHERE `DELIVERY_ID` = d.`DELIVERY_ID`
AND `FINISH_DATE` IS NULL) AS REMAINING_ORDERS
FROM `COURIERS` AS c
LEFT JOIN `DELIVERIES` AS d USING (`COURIER_ID`)
LEFT JOIN `COORDINATES` AS r ON r.`DELIVERY_ID` = d.`DELIVERY_ID`
WHERE (CASE WHEN
(SELECT MAX(`START_DATE`)
FROM `DELIVERIES`
WHERE `COURIER_ID` = c.`COURIER_ID`) IS NULL THEN d.`START_DATE` IS NULL ELSE d.`START_DATE` =
(SELECT MAX(`START_DATE`)
FROM `DELIVERIES`
WHERE `COURIER_ID` = c.`COURIER_ID`) END)
AND (CASE WHEN
(SELECT MAX(`DATE`)
FROM `COORDINATES`
WHERE `DELIVERY_ID` = d.`DELIVERY_ID`) IS NULL THEN r.`DATE` IS NULL ELSE r.`DATE` =
(SELECT MAX(`DATE`)
FROM `COORDINATES`
WHERE `DELIVERY_ID` = d.`DELIVERY_ID`) END)
GROUP BY c.`COURIER_ID`
ORDER BY d.`START_DATE` DESC
The problem is that this query is very slow (from 5 to 20 seconds) when I have over 5k COORDINATES and it does not returns all couriers sometimes.
Thank you so much for any solution.
Try this:
SELECT C.COURIER_ID, D.DELIVERY_ID, D.START_DATE, D.FINISH_DATE,
B.LAT, B.LNG, B.DATE, C.NoOfOrders
FROM COURIERS C
LEFT JOIN ( SELECT *
FROM (SELECT *
FROM DELIVERIES D
ORDER BY D.COURIER_ID, D.START_DATE DESC
) A
GROUP BY COURIER_ID
) AS A ON C.COURIER_ID = A.COURIER_ID
LEFT JOIN ( SELECT *
FROM (SELECT *
FROM COORDINATES CO
ORDER BY CO.DELIVERY_ID, CO.DATE DESC
) B
GROUP BY CO.DELIVERY_ID
) AS B ON A.DELIVERY_ID = B.DELIVERY_ID
LEFT JOIN ( SELECT O.DELIVERY_ID, COUNT(1) NoOfOrders
FROM ORDERS O WHERE FINISH_DATE IS NULL
GROUP BY O.DELIVERY_ID
) AS C ON A.DELIVERY_ID = C.DELIVERY_ID;
I haven't been able to test this query since I don't have a mysql database set up right now, much less with this schema and sample data. But I think this will work for you:
select
c.courier_id
, d.delivery_id
, co.lat
, co.lng
, oc.cnt as remaining_orders
from
couriers c
left join (
select
d.delivery_id
, d.courier_id
from
deliveries d
inner join (
select
d.delivery_id
, max(d.start_date) as start_date
from
deliveries d
group by
d.delivery_id
) dmax on dmax.delivery_id = d.delivery_id and dmax.start_date = d.start_date
) d on d.courier_id = c.courier_id
left join (
select
c.delivery_id
, c.lat
, c.lng
from
coordinates c
inner join (
select
c.delivery_id
, max(c.date) as date
from
coordinates c
group by
c.delivery_id
) cmax on cmax.delivery_id = c.delivery_id and cmax.date = c.date
) co on co.delivery_id = d.delivery_id
left join (
select
o.delivery_id
, count(o.order_id) as cnt
from
orders o
where
o.finish_date is null
group by
o.delivery_id
) oc on oc.delivery_id = d.delivery_id

Difficult MySQL Query - Getting Max difference between dates

I have a MySQL table of the following form
account_id | call_date
1 2013-06-07
1 2013-06-09
1 2013-06-21
2 2012-05-01
2 2012-05-02
2 2012-05-06
I want to write a MySQL query that will get the maximum difference (in days) between successive dates in call_date for each account_id. So for the above example, the result of this query would be
account_id | max_diff
1 12
2 4
I'm not sure how to do this. Is this even possible to do in a MySQL query?
I can do datediff(max(call_date),min(call_date)) but this would ignore dates in between the first and last call dates. I need some way of getting the datediff() between each successive call_date for each account_id, then finding the maximum of those.
I'm sure fp's answer will be faster, but just for fun...
SELECT account_id
, MAX(diff) max_diff
FROM
( SELECT x.account_id
, DATEDIFF(MIN(y.call_date),x.call_date) diff
FROM my_table x
JOIN my_table y
ON y.account_id = x.account_id
AND y.call_date > x.call_date
GROUP
BY x.account_id
, x.call_date
) z
GROUP
BY account_id;
CREATE TABLE t
(`account_id` int, `call_date` date)
;
INSERT INTO t
(`account_id`, `call_date`)
VALUES
(1, '2013-06-07'),
(1, '2013-06-09'),
(1, '2013-06-21'),
(2, '2012-05-01'),
(2, '2012-05-02'),
(2, '2012-05-06')
;
select account_id, max(diff) from (
select
account_id,
timestampdiff(day, coalesce(#prev, call_date), call_date) diff,
#prev := call_date
from
t
, (select #prev:=null) v
order by account_id, call_date
) sq
group by account_id
| ACCOUNT_ID | MAX(DIFF) |
|------------|-----------|
| 1 | 12 |
| 2 | 4 |
see it working live in an sqlfiddle
If you have an index on account_id, call_date, then you can do this rather efficiently without variables:
select account_id, max(call_date - prev_call_date) as diff
from (select t.*,
(select t2.call_date
from table t2
where t2.account_id = t.account_id and t2.call_date < t.call_date
order by t2.call_date desc
limit 1
) as prev_call_date
from table t
) t
group by account_id;
Just for educational purposes, doing it with JOIN:
SELECT t1.account_id,
MAX(DATEDIFF(t2.call_date, t1.call_date)) AS max_diff
FROM t t1
LEFT JOIN t t2
ON t2.account_id = t1.account_id
AND t2.call_date > t1.call_date
LEFT JOIN t t3
ON t3.account_id = t1.account_id
AND t3.call_date > t1.call_date
AND t3.call_date < t2.call_date
WHERE t3.account_id IS NULL
GROUP BY t1.account_id
Since you didn't specify, this shows max_diff of NULL for accounts with only 1 call.
SELECT a1.account_id , max(a1.call_date - a2.call_date)
FROM account a2, account a1
WHERE a1.account_id = a2.account_id
AND a1.call_date > a2.call_date
AND NOT EXISTS
(SELECT 1 FROM account a3 WHERE a1.call_date > a3.call_date AND a2.call_date < a3.call_date)
GROUP BY a1.account_id
Which gives :
ACCOUNT_ID MAX(A1.CALL_DATE - A2.CALL_DATE)
1 12
2 4

adding INNER JOIN to convert ID to a value from another table

i have an SQL Script which generates the following Output:
+------------+------------+--------------------+----------------------+---------------------+
| CUSTOMERID | TOTALCOUNT | VALIDWARRANTYCOUNT | EXPIREDWARRANTYCOUNT | LASTPURCHASED |
+------------+------------+--------------------+----------------------+---------------------+
| 1 | 5 | 5 | 0 | 2013-12-24 14:37:45 |
| 2 | 3 | 3 | 0 | 2013-12-24 14:37:45 |
| 3 | 6 | 6 | 0 | 2013-10-23 13:37:45 |
+------------+------------+--------------------+----------------------+---------------------+
I would like to see the Companyname of the Customer in a additional column. I have an extra table with customer id and company name but I don't know how to extend my query. All trys result in an empty table.
Here is my Query:
SELECT p2c.customerid
, (
SELECT COUNT(*)
FROM products2customers
WHERE customerid = p2c.customerid
) AS TotalCount
, (
SELECT COUNT(*)
FROM products2customers
WHERE customerid = p2c.customerid
AND date_add(from_unixtime(purchased), INTERVAL 5 YEAR) >= CURDATE()
) AS ValidWarrantyCount
, (
SELECT COUNT(*)
FROM products2customers
WHERE customerid = p2c.customerid
AND date_add(from_unixtime(purchased), INTERVAL 5 YEAR) < CURDATE()
) AS ExpiredWarrantyCount
, (
SELECT MAX( from_unixtime(purchased) )
FROM products2customers
WHERE customerid = p2c.customerid
) AS LastPurchased
FROM (
SELECT DISTINCT
p2c.customerid
FROM
products2customers p2c
INNER JOIN
products p
ON
p2c.customerid = p.id
) AS p2c
I made an SQL Fiddle with the normal query without a join, I hope someone could give me a hint.
SQLFiddle
Just add your JOIN in the very last line, and select the company after the customer id:
SELECT p2c.customerid,
c.company
, (
SELECT COUNT(*)
FROM products2customers
WHERE customerid = p2c.customerid
) AS TotalCount
, (
SELECT COUNT(*)
FROM products2customers
WHERE customerid = p2c.customerid
AND date_add(from_unixtime(purchased), INTERVAL 5 YEAR) >= CURDATE()
) AS ValidWarrantyCount
, (
SELECT COUNT(*)
FROM products2customers
WHERE customerid = p2c.customerid
AND date_add(from_unixtime(purchased), INTERVAL 5 YEAR) < CURDATE()
) AS ExpiredWarrantyCount
, (
SELECT MAX( from_unixtime(purchased) )
FROM products2customers
WHERE customerid = p2c.customerid
) AS LastPurchased
FROM (
SELECT DISTINCT
p2c.customerid
FROM
products2customers p2c
) AS p2c
JOIN customers c ON c.id = p2c.customerid; <--
Updated fiddle:
http://sqlfiddle.com/#!2/60396/5/0
just add another join to the customer table as below
SELECT p2c.customerid, company
, (
SELECT COUNT(*)
FROM products2customers
WHERE customerid = p2c.customerid
) AS TotalCount
, (
SELECT COUNT(*)
FROM products2customers
WHERE customerid = p2c.customerid
AND date_add(from_unixtime(purchased), INTERVAL 5 YEAR) >= CURDATE()
) AS ValidWarrantyCount
, (
SELECT COUNT(*)
FROM products2customers
WHERE customerid = p2c.customerid
AND date_add(from_unixtime(purchased), INTERVAL 5 YEAR) < CURDATE()
) AS ExpiredWarrantyCount
, (
SELECT MAX( from_unixtime(purchased) )
FROM products2customers
WHERE customerid = p2c.customerid
) AS LastPurchased
FROM (
SELECT
p2c.customerid, c.company
FROM
products2customers p2c
INNER JOIN
products p
ON
p2c.customerid = p.id
inner join customers c
on c.id=p2c.customerid group by 1,2
) AS p2c