This is a small snippet of my table, which currently contains ~10,000,000 rows
+---------+---------------------+-----------+----------------+
| card_id | date | avg_price | foil_avg_price |
+---------+---------------------+-----------+----------------+
| 10000 | 2014-06-28 09:05:56 | 5.02 | 10.22 |
| 20000 | 2014-06-28 09:05:54 | 14.58 | 25.10 |
| 10000 | 2014-06-29 09:05:56 | 0.00 | 19.62 |
| 20000 | 2014-06-29 09:05:54 | 14.58 | 0.00 |
| 10000 | 2014-07-01 09:05:56 | 0.00 | 19.62 |
| 20000 | 2014-07-01 09:05:54 | 0.00 | 25.10 |
+---------+---------------------+-----------+----------------+
It is a price history for cards, including what the avg_price and what the foil_avg_price was for each day or so.
I'd like to select, for a group of card id's the most recent date when the foil_avg_price was > 0, what that price was, and the most recent date that the avg_price was > 0, and what that price was. My resulting data set for the above would look something like this:
+---------+---------------------+-----------+---------------------+----------------+
| card_id | avg_date | avg_price | foil_date | foil_avg_price |
+---------+---------------------+-----------+---------------------+----------------+
| 10000 | 2014-06-28 09:05:56 | 5.02 | 2014-07-01 09:05:54 | 19.62 |
| 20000 | 2014-06-29 09:05:54 | 14.58 | 2014-07-01 09:05:54 | 25.10 |
+---------+---------------------+-----------+---------------------+----------------+
I'm sure that this involves an INNER JOIN on the same table but I can't quite get my head around it. Any help would be much appreciated.
Three steps:
Find last price date
Find last foil price date
resolve prices on these dates
So,
SELECT dates.*, price.avg_price, foilprice.foil_avg_price
FROM (
SELECT
card_id,
MAX(IF(avg_price>0, `date`, '0001-01-01')) AS avg_date,
MAX(IF(foil_avg_price>0, `date`, '0001-01-01')) AS foil_avg_date
FROM card_price
GROUP BY card_id
) AS dates
INNER JOIN card_price AS price
ON dates.card_id=price.`date`
INNER JOIN card_price AS foilprice
ON dates.card_id=foilprice.`date`
Try this query
SELECT A.card_id,max(date),MAX(avg_price), (SELECT MAX(date) FROM test WHERE card_id = A.card_id AND foil_avg_price = MAX(A.foil_avg_price)) AS date,MAX(foil_avg_price) FROM test A
GROUP BY A.card_id
How about if you had 20,000,000 rows...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(card_id INT NOT NULL
,date DATETIME NOT NULL
,price_type VARCHAR(20) NOT NULL
,price_value DECIMAL(5,2) NOT NULL
,PRIMARY KEY(card_id,date,price_type)
);
INSERT INTO my_table VALUES
(10000,'2014-06-28 09:05:56','avg_price',5.02),
(20000,'2014-06-28 09:05:54','avg_price',14.58),
(10000,'2014-06-29 09:05:56','avg_price',0.00),
(20000,'2014-06-29 09:05:54','avg_price',14.58),
(10000,'2014-07-01 09:05:56','avg_price',0.00),
(20000,'2014-07-01 09:05:54','avg_price',0.00),
(10000,'2014-06-28 09:05:56','foil_avg_price',10.22),
(20000,'2014-06-28 09:05:54','foil_avg_price',25.10),
(10000,'2014-06-29 09:05:56','foil_avg_price',19.62),
(20000,'2014-06-29 09:05:54','foil_avg_price',0.00),
(10000,'2014-07-01 09:05:56','foil_avg_price',19.62),
(20000,'2014-07-01 09:05:54','foil_avg_price',25.10);
SELECT x.*
FROM my_table x
JOIN
( SELECT card_id,price_type,MAX(date) max_date FROM my_table WHERE price_value > 0 GROUP BY card_id,price_type) y
ON y.card_id = x.card_id
AND y.price_type = x.price_type
AND y.max_date = x.date;
+---------+---------------------+----------------+-------------+
| card_id | date | price_type | price_value |
+---------+---------------------+----------------+-------------+
| 10000 | 2014-06-28 09:05:56 | avg_price | 5.02 |
| 10000 | 2014-07-01 09:05:56 | foil_avg_price | 19.62 |
| 20000 | 2014-06-29 09:05:54 | avg_price | 14.58 |
| 20000 | 2014-07-01 09:05:54 | foil_avg_price | 25.10 |
+---------+---------------------+----------------+-------------+
Try this:
SELECT a.card_id, a.avg_date, a.avg_price, b.foil_date, b.foil_avg_price
FROM (SELECT c.card_id, c.date AS avg_date, c.avg_price
FROM cards c
INNER JOIN (SELECT c.card_id, MAX(IF(c.avg_price > 0, c.date, NULL)) avg_date
FROM cards c GROUP BY c.card_id
) a ON c.card_id = a.card_id AND c.date = a.avg_date
) AS a
LEFT JOIN (SELECT c.card_id, c.date AS foil_date, c.foil_avg_price
FROM cards c
INNER JOIN (SELECT c.card_id, MAX(IF(c.foil_avg_price > 0, c.date, NULL)) foil_date
FROM cards c GROUP BY c.card_id
) a ON c.card_id = a.card_id AND c.date = a.foil_date
) AS b ON a.card_id = b.card_id ;
OR
SELECT a.card_id, a.avg_date, a.avg_price, b.foil_date, b.foil_avg_price
FROM (SELECT *
FROM (SELECT c.card_id, c.date, c.avg_price
FROM cards c WHERE c.avg_price > 0
ORDER BY c.date DESC
) AS A
GROUP BY A.date
) AS a
LEFT JOIN ( SELECT *
FROM (SELECT c.card_id, c.date, c.foil_avg_price
FROM cards c WHERE c.foil_avg_price > 0
ORDER BY c.date DESC
) AS B
GROUP BY B.date
) AS b ON a.card_id = b.card_id;
Related
I'm having a challenge to get the monthly total sum of amount_tendered from both shop1 table and shop2 table, and the monthly total sum of payment_amount from payments table.
and if payments don't have a value for the month it should show zero.
shop1
--------------------------------------------------------
| trans_id | amount_tendered | trans_date |
--------------------------------------------------------
| 1 | 10.00 | 2020-09-03 06:09:55 |
| 2 | 15.00 | 2020-08-01 10:19:01 |
--------------------------------------------------------
shop2
--------------------------------------------------------
| trans_id | amount_tendered | trans_date |
--------------------------------------------------------
| 1 | 30.00 | 2020-09-01 16:09:55 |
| 2 | 15.00 | 2020-09-11 11:19:01 |
--------------------------------------------------------
Payments
------------------------------------------------------------
| payments_id | payment_amount | payment_date |
------------------------------------------------------------
| 1 | 100.00 | 2020-09-01 16:09:55 |
| 2 | 105.00 | 2020-09-11 11:19:01 |
------------------------------------------------------------
SELECT t1.yr, t1.mnth, ifnull(t2.total_trans,0), ifnull(t3.payments,0) FROM
(SELECT YEAR(trans_date) as yr,
MONTHNAME(trans_date) as mnth,
FROM shop1
GROUP BY YEAR(trans_date), MONTHNAME(trans_date)
ORDER BY YEAR(trans_date), MONTHNAME(trans_date)) as t1
LEFT JOIN (
SELECT(
(SUM(amount_tendered) FROM shop1 GROUP BY YEAR(trans_date), MONTHNAME(trans_date)+
(SUM(amount_tendered) FROM shop2 GROUP BY YEAR(trans_date), MONTHNAME(trans_date)
) as 'total_trans'
)as t2
LEFT JOIN (
SELECT SUM(payment_amount ) FROM transactions GROUP BY YEAR(payment_date), MONTHNAME(payment_date) as payments
)as t3
The expected result
------------------------------------------------------------
| yr | mnth | total_trans | payments |
------------------------------------------------------------
| 2020 | August | 15.00 | 0.00 |
| 2020 | September| 55.00 | 105.00 |
------------------------------------------------------------
Error : Syntax error near 'FROM transactions GROUP BY YEAR(transaction_date), MONTHNAME(transaction_date) ' at line 4
You would typically compute the monthly sum in two separate subqueries, and then join the results.
I am not a fan of having one table per shop: having several tables with the same columns usually indicates a design problem. Here, we use union all to collect data from both tables before aggregating.
select t.*, p.payments
from (
select year(trans_date) yr, monthname(trans_date) mnth, sum(amount_tendered) total_trans
from (
select trans_date, amount_tendered from shop1
union all
select trans_date, amount_tendered from shop2
) t
group by year(trans_date), monthname(trans_date)
) t
left join (
select year(payment_date) yr, monthname(payment_date) mnth, sum(payment_amount) payments
from payments
group by year(payment_date), monthname(payment_date)
) p on p.yr = t.yr and p.mnth = t.mnth
The left join avoids filtering out months that have no transactions.
I want to summarize the sales data and I want to sum its total in the last row, I'm using "GROUP BY" and "WITH ROLLUP" but the results are:
+--------+--------------------+------------+--------+-----------+
| id | name | date | amount | total |
+--------+--------------------+------------+--------+-----------+
| Z00015 | Mebel Harmonis | 2019-05-09 | 2 | 10000000 |
| Z00016 | Mebel Harmonis | 2019-05-09 | 10 | 45000000 |
| Z00017 | Mebel Tunggal Jaya | 2019-05-10 | 3 | 12000000 |
| (null) | Mebel Tunggal Jaya | 2019-05-10 | 29 | 131000000 |
+--------+--------------------+------------+--------+-----------+
the last row that i want:
+--------+--------+--------+----+-----------+
| (null) | (null) | (null) | 29 | 131000000 |
+--------+--------+--------+----+-----------+
This is my query:
SELECT
order2.id_order AS id,
customer.name_customer AS name,
DATE( order2.date_order ) AS date ,
Count( order_detail.id_detail ) AS amount,
SUM( harga ) AS total
FROM
order_detail
INNER JOIN order2 ON order2.id_order = order_detail.id_order
INNER JOIN customer ON order2.id_customer = customer.id_customer
INNER JOIN produk ON produk.id_produk = order_detail.id_produk
INNER JOIN sofa ON sofa.id_sofa = produk.id_sofa
WHERE
date( date_order ) >= '2019-05-01'
AND date( date_order ) <= '2019-05-31'
GROUP BY
order2.id_order WITH ROLLUP;
You need to specify all the columns that can be combined together for the grand total in your GROUP BY clause:
GROUP BY id, name, date WITH ROLLUP
However, this will create intermediate subtotals for each id and id, name. You can filter them out with:
HAVING id IS NOT NULL OR (id IS NULL AND name IS NULL AND date IS NULL)
I have a table with Transactions, amongst whose columns are id, created_at, and company_id. I'd like to group the four first transactions of every company and return the created_at values of each transaction on each row.
In other words, I want each row of my output to correspond to the four first transactions of each company (so grouping by company_id) with columns showing me the company_id and the created_at of each of those four transactions.
How do I do that?
Sample data:
id | company_id | created_at
---------------------------------
1123 | abcd | 10/12/2015
8291 | abcd | 10/14/2015
9012 | abcd | 10/15/2015
9540 | abcd | 10/16/2015
10342 | abcd | 10/21/2015
10456 | abcd | 10/22/2015
2301 | efgh | 10/13/2015
4000 | efgh | 11/01/2015
4023 | efgh | 11/03/2015
6239 | efgh | 11/08/2015
7500 | efgh | 11/14/2015
Sample output:
company_id | created_at_1 | created_at_2 | created_at_3 | created_at_4
--------------------------------------------------------------------------
abcd | 10/12/2015 | 10/14/2015 | 10/15/2015 | 10/16/2015
efgh | 10/13/2015 | 11/01/2015 | 11/03/2015 | 11/08/2015
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,company_id VARCHAR(12) NOT NULL
,created_at DATE NOT NULL
);
INSERT INTO my_table VALUES
( 1123,'abcd','2015/10/12'),
( 8291,'abcd','2015/10/14'),
( 9012,'abcd','2015/10/15'),
( 9540,'abcd','2015/10/16'),
(10342,'abcd','2015/10/21'),
(10456,'abcd','2015/10/22'),
( 2301,'efgh','2015/10/13'),
( 4000,'efgh','2015/11/01'),
( 4023,'efgh','2015/11/03'),
( 6239,'efgh','2015/11/08'),
( 7500,'efgh','2015/11/14');
SELECT x.*
FROM my_table x
JOIN my_table y
ON y.company_id = x.company_id
AND y.created_at <= x.created_at
GROUP
BY x.id
HAVING COUNT(*) <= 4
ORDER
BY company_id
, created_at;
+------+------------+------------+
| id | company_id | created_at |
+------+------------+------------+
| 1123 | abcd | 2015-10-12 |
| 8291 | abcd | 2015-10-14 |
| 9012 | abcd | 2015-10-15 |
| 9540 | abcd | 2015-10-16 |
| 2301 | efgh | 2015-10-13 |
| 4000 | efgh | 2015-11-01 |
| 4023 | efgh | 2015-11-03 |
| 6239 | efgh | 2015-11-08 |
+------+------------+------------+
A solution with variables will be orders of magnitude faster, e.g...
SELECT a.id
, a.company_id
, a.created_at
FROM
( SELECT x.*
, CASE WHEN #prev = x.company_id THEN #i:=#i+1 ELSE #i:=1 END i, #prev:=x.company_id prev
FROM my_table x
, (SELECT #i:=1,#prev:=null) vars
ORDER
BY x.company_id
, x.created_at
) a
WHERE i <= 4;
One possible way is the following:
select company_id,
min(created_at) as created_at_1,
(select created_at from t where company_id=t1.company_id order by created_at limit 1 offset 1) as created_at_2,
(select created_at from t where company_id=t1.company_id order by created_at limit 1 offset 2) as created_at_3,
(select created_at from t where company_id=t1.company_id order by created_at limit 1 offset 3) as created_at_4
from t as t1
group by company_id
EDIT:
Another possibility (inspired by this answer) is:
select company_id,
min(created_at) as created_at_1,
min(case r when 2 then created_at else null end) as created_at_2,
min(case r when 3 then created_at else null end) as created_at_3,
min(case r when 4 then created_at else null end) as created_at_4
from (
select company_id, created_at,
(case company_id when #curType
then #curRank := #curRank + 1
else #curRank := 1 and #curType := company_id end)+1 as r
from t, (select #curRank := 0, #curType := '') f
order by company_id, created_at
) as o
where r <= 4
group by company_id
Could by like this maybe?
SELECT S.company_id,
A.created_at created_at_1,
B.created_at created_at_2,
C.created_at created_at_3,
D.created_at created_at_4
FROM sample S
LEFT JOIN sample A on S.company_id = A.company_id AND A.id NOT IN(S.id)
LEFT JOIN sample B on S.company_id = B.company_id AND B.id NOT IN(S.id, A.id)
LEFT JOIN sample C on S.company_id = C.company_id AND C.id NOT IN(S.id, A.id, B.id)
LEFT JOIN sample D on S.company_id = D.company_id AND D.id NOT IN(S.id, A.id, B.id, C.id)
GROUP BY S.company_id
http://sqlfiddle.com/#!9/c577e/3
It might not be very efficient, though.
And they are not in order, because your American date format is not good to be sorted. Better switch to TIMESTAMP format.
Table data looks like:
EventID | MPID | rundate | Horizon | otherData
1 | 1 | 23-Jun-2014 | 360 | other value
1 | 1 | 23-Jun-2014 | 365 | pther value
1 | 1 | 23-Jun-2014 | 300 | pther value
1 | 1 | 22-Jun-2014 | 700 | pther value
1 | 2 | 23-Jun-2014 | 400 | other value
1 | 2 | 23-Jun-2014 | 340 | oth
2 | 3 | 23-Jun-2014 | 360 | pther value
2 | 3 | 23-Jun-2014 | 300 | pther value
2 | 3 | 22-Jun-2014 | 365 | pther value
I want to select the max rundate for each event and marketplace group and then select max horizon among that group and then print the entire row.
Desired Result is :
EventID | MPID | rundate | Horizon | otherData
1 | 1 | 23-Jun-2014 | 365 | pther value
1 | 2 | 23-Jun-2014 | 400 | other value
2 | 3 | 23-Jun-2014 | 360 | pther value
Please let me know the SQL query for this.
I tried following query but its not working:
SELECT * from dsie_result_overalls where id in (
SELECT k.id from dsie_result_overalls k,
(
SELECT a.event_id, a.marketplaceid, MAX(a.horizon) as horizon FROM dsie_result_overalls a,
(
SELECT id, event_id, marketplaceid, MAX(rundate) AS rundate FROM dsie_result_overalls
GROUP BY event_id, marketplaceid
) b
WHERE a.event_id = b.event_id AND a.marketplaceid = b.marketplaceid AND a.rundate = b.rundate
GROUP BY a.event_id, a.marketplaceid
) l WHERE k.event_id = l.event_id AND k.marketplaceid = l.marketplaceid AND k.horizon = l.horizon
);
It selects the multiple rundate for max horizon.
Try this query
Select T.* From Tbl T JOIN
( Select Max(S.Horizon) MaxHorizon,Max(S.rundate) As dte,S.EventID,S.MPID
From Tbl S Join
( Select T1.EventID,Max(T1.rundate) As Maxrundate,T1.MPID
From Tbl T1 Group By T1.EventID,T1.MPID
) JR On S.rundate = JR.Maxrundate AND S.EventID = JR.EventID AND S.MPID = JR.MPID
Group By S.MPID,S.EventID
)R ON T.Horizon = R.MaxHorizon AND T.EventID = R.EventID AND T.MPID = R.MPID AND T.rundate = R.dte
Fiddle Demo
Output would be
EventID | MPID | rundate | Horizon | otherData
1 | 1 | 23-Jun-2014 | 365 | pther value
1 | 2 | 23-Jun-2014 | 400 | other value
2 | 3 | 23-Jun-2014 | 360 | pther value
The proper way...
SELECT x.*
FROM dsie_result_overalls x
JOIN
( SELECT a.eventid
, a.mpid
, a.rundate
, MAX(a.horizon) max_horizon
FROM dsie_result_overalls a
JOIN
( SELECT eventid
, mpid
, MAX(rundate) max_rundate
FROM dsie_result_overalls
GROUP
BY eventid
, mpid
) b
ON b.eventid = a.eventid
AND b.mpid = a.mpid
AND b.max_rundate = a.rundate
GROUP
BY a.eventid
, a.mpid
, a.rundate
) y
ON y.eventid = x.eventid
AND y.mpid = x.mpid
AND y.rundate = x.rundate
AND y.max_horizon = x.horizon;
The hack way...
SELECT *
FROM
( SELECT *
FROM dsie_result_overalls
ORDER
BY eventid
, mpid
, rundate DESC
, horizon DESC
) x
GROUP
BY eventid
, mpid;
The old-fashioned way...
SELECT x.*
FROM dsie_result_overalls x
LEFT
JOIN dsie_result_overalls y
ON y.eventid = x.eventid
AND y.mpid = x.mpid
AND (y.rundate > x.rundate OR (y.rundate = x.rundate AND y.horizon > x.horizon))
WHERE y.id IS NULL;
You can group by event and marketplace an get the MAX(rundate). The MAX(horizon) you can get follow.
SELECT eventid
, mpid
, MAX(rundate) rundate
, SUBSTRING_INDEX(GROUP_CONCAT(horizon ORDER BY rundate DESC, horizon DESC),',',1) horizon
FROM dsie_result_overalls
GROUP
BY eventid
, mpid
I have a table order_history that is similar to the following:
+-------------------------------------------------------------+
| order_history_id | order_id | order_status_id | date_addded |
+-------------------------------------------------------------+
| 1 | 1 | 1 | 2014-03-20 |
| 2 | 1 | 2 | 2014-03-21 |
| 3 | 1 | 3 | 2014-03-29 |
| 4 | 2 | 1 | 2014-03-20 |
| 5 | 2 | 2 | 2014-03-21 |
| 6 | 2 | 3 | 2014-04-02 |
| 7 | 3 | 1 | 2014-04-20 |
| 8 | 3 | 2 | 2014-04-21 |
| 9 | 3 | 3 | 2014-04-22 |
+-------------------------------------------------------------+
The order_status represents the status of an order
+-------------------------------+
| order_status_id | name |
+-------------------------------+
| 1 | received |
| 2 | processed |
| 3 | shipped |
+-------------------------------+
what i want to do is to pull out all the orders that have been received before 2014-04-01 but not shipped until after 2014-04-01.
So in this case the query would just return order_id 2 as this is the only order that was received before 2014-04-01 yet shipped after.
I can't even seem to get started... Any help, hints, or pointers much appreciated.
You can do so ,by joining your tables and count the statues shipped for each order by using expression in sum i.e SUM(os.name ='shipped') shipped
SELECT o.*
,SUM(os.name ='shipped') shipped
FROM
orders o
LEFT JOIN orders_status os USING(order_status_id)
WHERE o.date_addded < '2014-04-01'
GROUP BY o.order_id
HAVING shipped =0
Fiddle Demo
You can use INNER JOIN with this, if I get what you really want you can try this:
SELECT DISTINCT order_id
FROM order_history A
INNER JOIN order_status B
ON A.order_status_id = B.order_status_id
WHERE (A.order_Status_id = '1' AND A.date_added < #date) AND (A.order_status_id = '3' AND A.date_added < #date)
SELECT h1.order_id
FROM order_history h1
JOIN order_status s1
ON s1.order_status_id = h1.order_status_id
JOIN order_history h2
ON h2.order_id = h1.order_id
JOIN order_status s2
ON s2.order_status_id = h2.order_status_id
WHERE h1.date_addded < '2014-04-01'
AND s1.name = 'received'
AND h2.date_addded >= '2014-04-01'
AND s2.name = 'shipped';
Note: Too many 'd's in addded
SELECT r.order_id
FROM (
SELECT DISTINCT oh.order_id
FROM order_history AS oh
JOIN order_status AS os ON(oh.order_status_id = os.order_status_id)
WHERE os.name = 'received'
AND oh.date_addded < '2014-04-01'
) AS r
JOIN (
SELECT DISTINCT oh.order_id
FROM order_history AS oh
JOIN order_status AS os ON(oh.order_status_id = os.order_status_id)
WHERE os.name = 'shipped'
AND oh.date_addded > '2014-04-01'
) AS s ON (s.order_id = r.order_id)
demo
What about this simple and light query:
SELECT DISTINCT order_id
FROM order_history o1
JOIN order_history o2
ON o1.order_id = o2.order_id
AND o1.order_status_id=1 AND o1.date_added<'2014-04-01'
AND o2.order_status_id=3 AND o2.date_added>'2014-04-01';
Not tested, but try this:
SELECT A.ORDER_ID
FROM ORDER_HISTORY A, ORDER_HISTORY B
WHERE A.ORDER_ID = B.ORDER_ID
AND A.order_status_id = 1
AND A.date_addded < TO_DATETO_DATE ('2014-04-01', 'YYYY-MM-DD')
AND B.order_status_id = 3
AND B.date_addded > TO_DATETO_DATE ('2014-04-01', 'YYYY-MM-DD');