How to find latest effective price for each month DB2 SQL - sql

I`m trying to build a query that will the latest established price for each month within a year for two price types. A price may or may not change each month for each item and type. Below is my source table
item | date | type | price
------------------------------
itm1 | 20180101 | 1 | 3
itm1 | 20180101 | 2 | 1
itm1 | 20180105 | 1 | 5
itm2 | 20180101 | 1 | 8
itm2 | 20180103 | 2 | 6
itm2 | 20180105 | 2 | 5
itm3 | 20171215 | 1 | 7
itm3 | 20180201 | 1 | 9
itm3 | 20180201 | 2 | 10
And this is what I`m trying to achieve
item | YYYMM |type1_prc | type1_last(max) |type2_prc | typ2_last(max)
| | | effective_date | | effective_date
---------------------------------------------------------------------------
itm1 | 201801 | 5 | 20180105 | 1 | 20180101
itm2 | 201801 | 8 | 20180101 | 5 | 20180105
itm3 | 201801 | 7 | 20171215 | - | -
itm1 | 201802 | 5 | 20180105 | 1 | 20180101
itm2 | 201802 | 8 | 20180101 | 5 | 20180105
itm3 | 201802 | 9 | 20180201 | 10 | 20180201
Thank you!

DB2 doesn't have (to the best of my knowledge) aggregation functions for getting the first element. One relatively simple way is to use the first_value() window function:
select distinct item, to_char(date, 'YYYY-MM') as yyyymm,
first_value(price) over (partition by item, to_char(date, 'YYYY-MM') order by type asc) as type1_price,
max(case when type = 1 then price end) as type1_date
first_value(price) over (partition by item, to_char(date, 'YYYY-MM') order by type desc) as type2_price,
max(case when type = 2 then price end) as type2_date
from t

Related

SQL Select Day IN and Day OUT grouped by ID's

How to GROUP EIDs by dates where Date between 2014-01-15 and 2014-03-18
| ID |EID | DATE | Status | |
|----------|--------------|---------|-----|
| 9 |9991 | 2014-03-16 | OUT | |
| 8 |9997 | 2014-03-18 | IN | |
| 7 |9997 | 2014-03-16 | OUT | |
| 6 |9999 | 2014-02-16 | IN | |
| 5 |9999 | 2014-02-16 | OUT | |
| 4 |9996 | 2014-03-18 | IN | |
| 3 |9996 | 2014-03-16 | OUT | |
| 2 |9997 | 2014-01-18 | IN | |
| 1 |9997 | 2014-01-15 | OUT | |
Output should be like:
|
|EID |in date | OUT date| DAYS OUT |
|------|--------------|--------- |------ ----|
| 9997 | 2014-03-18 | 2014-03-16| 2 |
| 9997 | 2014-01-18 | 2014-01-15| 3 |
| 9999 | 2014-02-16 | 2014-02-16| 0 |
| 9996 | 2014-03-18 | 2014-03-16| 2 |
| 9991 | | 2014-03-16| |
Thank you
Here is one method that assumes that they are interleaved, so no two ins or outs are together:
select eid,
max(case when status = 'in' then date end) as in_date,
max(case when status = 'out' then date end) as out_date,
datediff(day,
max(case when status = 'in' then date end),
max(case when status = 'out' then date end)
) as days_diff
from (select t.*, row_number() over (partition by eid, status order by date) as seqnum
from t
) t
group by eid, seqnum;
I think that you have already done it but, have you tried to do the sentence like:
SELECT [here you format as you wish] FROM [your table] WHERE date BETWEEN '2014-01-15' AND '2014-03-18' GROUP BY date
or
SELECT [here you format as you wish] FROM [your table] WHERE dateIn >= '2014-01-15' AND dateOut <= '2014-03-18' GROUP BY dateIn
Can you share your full table?

SQL Duplicating tables with groupBy

I'm trying to compare income/outgoings using a simple query, but for some reason, I'm getting duplicated data. This is the query I'm running:
SELECT
Event.Name as "Event",
Concat("£", round(sum(Ticket.Price),2)) as "Ticket Sales",
sum(Invoice.NetTotal) as "Invoice Costs",
Concat("£", round(sum(Ticket.Price),2) - round(sum(Invoice.NetTotal),2)) as "Total Loss"
FROM Ticket
JOIN Event ON Ticket.EventID = Event.EventID
JOIN Invoice ON Event.EventID = Invoice.EventID
GROUP BY Event.EventID;
This is the result I'm getting
+--------------------------+--------------+---------------+------------+
| Event | Ticket Sales | Invoice Costs | Total Loss |
+--------------------------+--------------+---------------+------------+
| Victorious Festival 2018 | £47.94 | 1800 | £-1752.06 |
+--------------------------+--------------+---------------+------------+
Despite there only being 2 items in the Invoice table, totaling £600,
and 3 relevant items in the ticket table totaling £24.97
+-----------+--------+---------+---------------+-------------+----------+------+
| InvoiceNo | ItemID | EventID | HireStartDate | HireEndDate | NetTotal | VAT |
+-----------+--------+---------+---------------+-------------+----------+------+
| 1 | 1 | 1 | 2018-05-05 | 2018-05-06 | 500 | 20 |
| 2 | 2 | 1 | 2018-05-05 | 2018-05-06 | 100 | 20 |
+-----------+--------+---------+---------------+-------------+----------+------+
+----------+---------+-------+------------+------------+----------+
| TicketNo | EventID | Price | ValidFrom | ValidTo | Class |
+----------+---------+-------+------------+------------+----------+
| 1 | 1 | 7.99 | 2018-05-05 | 2018-05-22 | Standard |
| 2 | 1 | 7.99 | 2018-05-05 | 2018-05-22 | Standard |
| 3 | 2 | 10 | 2018-04-28 | 2018-04-28 | Standard |
| 4 | 2 | 10 | 2018-04-28 | 2018-04-28 | Standard |
| 5 | 2 | 10 | 2018-04-28 | 2018-04-28 | Standard |
| 6 | 2 | 10 | 2018-04-28 | 2018-04-28 | Standard |
| 7 | 2 | 10 | 2018-04-28 | 2018-04-28 | Standard |
| 8 | 2 | 10 | 2018-04-28 | 2018-04-28 | Standard |
| 9 | 1 | 7.99 | 2018-05-05 | 2018-05-22 | Standard |
+----------+---------+-------+------------+------------+----------+
You have two different independent dimensions. The best solution is to aggregate before joining:
SELECT e.Name as "Event",
Concat("£", round(sum(t.Price), 2)) as "Ticket Sales",
sum(i.NetTotal) as "Invoice Costs",
Concat("£", round(sum(t.Price), 2) - round(sum(i.NetTotal), 2)) as "Total Loss"
FROM Event e JOIN
(SELECT t.EventId, SUM(Price) as Price
FROM Ticket t
GROUP BY t.EventId
) t
ON t.EventID = e.EventID JOIN
(SELECT i.EventId, SUM(i.NetTotal) as NetTotal
FROM Invoice i
GROUP BY i.EventId
) i
ON e.EventID = i.EventID
GROUP BY e.EventID;
Two comments. First, I don't really like aggregating on EventId, because it is not in the SELECT (preferring EventName instead). Assuming that it is the primary key for Events, then this structure is fine -- the id uniquely identifies each row in events, so the name is well-defined.
Second, you might want to make the joins left joins, so you are including all events, even those that might be missing tickets or invoices.

max(sum(field query in Hive/SQL

I have a table with lots of transactions for users across a month.
I need to take the hour from each day where Sum(cost) is at its highest.
I've tried MAX(SUM(Cost)) but get an error.
How would I go about doing this please?
here is some sample data
+-------------+------+----------+------+
| user id | hour | date | Cost |
+-------------+------+----------+------+
| 343252 | 13 | 20170101 | 21.5 |
| 32532532 | 13 | 20170101 | 22.5 |
| 35325325 | 13 | 20170101 | 30.5 |
| 325325325 | 13 | 20170101 | 10 |
| 64643643 | 12 | 20170101 | 22 |
| 643643643 | 12 | 20170101 | 31 |
| 436325234 | 13 | 20170101 | 15 |
| 213213213 | 13 | 20170101 | 12 |
| 53265436436 | 17 | 20170101 | 19 |
+-------------+------+----------+------+
Expected Output:
I need just one row per day, where it shows the total cost from the 'most expensive' hour. In this case, 13:00 had a total cost of 111.5
select hr
,dt
,total_cost
from (select dt
,hr
,sum(cost) as total_cost
,row_number () over
(
partition by dt
order by sum(cost) desc
) as rn
from mytable
group by dt,hr
) t
where rn = 1
+----+------------+------------+
| hr | dt | total_cost |
+----+------------+------------+
| 13 | 2017-01-01 | 111.5 |
+----+------------+------------+
Try this:
select AVG(hour) as 'Hour',date as 'Date',sum(cost) as 'TotalCost' from dbo.Table_3 group by date

Sql query for special record

At the first excuse me for my bad english.
I have two tables:
master table:
| product id | pr_name | remain_Qty |
+--------------+------------------+-------------------+
| 1 | x | 13 |
| 2 | y | 18 |
| 3 | z | 21 |
+--------------+------------------+-------------------+
Detail Table (This table contain detail data of bought product):
+--------------+------------------+----------+--------+
| date | pr_id | Qty |price |
+--------------+------------------+----------+--------+
| 2010-01-01 | 1 | 3 | 1000 |
| 2010-01-02 | 1 | 5 | 1200 |
| 2010-01-01 | 2 | 11 | 1100 |
| 2010-01-03 | 1 | 4 | 1400 |
| 2010-01-04 | 3 | 3 | 1300 |
| 2010-01-01 | 2 | 6 | 1600 |
| 2010-01-03 | 1 | 7 | 1700 |
| 2010-01-02 | 3 | 3 | 1300 |
| 2010-01-01 | 3 | 5 | 1500 |
| 2010-01-04 | 3 | 7 | 1700 |
| 2010-01-06 | 2 | 8 | 1800 |
| 2010-01-07 | 2 | 4 | 1400 |
| 2010-01-03 | 1 | 3 | 1300 |
| 2010-01-04 | 3 | 6 | 1600 |
| 2010-01-08 | 1 | 1 | 1100 |
+--------------+------------------+----------+--------+
sum Qty of product 1 = 23
sum Qty of product 2 = 29
sum Qty of product 3 = 21
As a result I want list of the Details table, where the list is sorted by pr_id , date and price, but the sum(Qty) per pr_id don't exceed the remain_Qty of the product_id of the Master table.
For example:
+--------------+------------------+----------+--------+
| date | pr_id | Qty |price |
+--------------+------------------+----------+--------+
| 2010-01-01 | 1 | 3 | 1000 |
| 2010-01-02 | 1 | 5 | 1200 |
| 2010-01-03 | 1 | 4 | 1400 |
| 2010-01-03 | 1 | 1 | 1700 |
| 2010-01-01 | 2 | 11 | 1100 |
| 2010-01-01 | 2 | 6 | 1600 |
| 2010-01-01 | 3 | 5 | 1500 |
| 2010-01-02 | 3 | 3 | 1300 |
| 2010-01-04 | 3 | 3 | 1300 |
| 2010-01-04 | 3 | 7 | 1700 |
+--------------+------------------+----------+--------+
More of a clarification than a direct SQL answer. But what it LOOKS like they may be wanting is based on an inventory being depleted to fill orders from the known available quantity, but even that falls short as the may be missing a second qty of 3 on 2010-01-03 for product 1... which if looking at just ID=1 from his sample data would show...
| date | pr_id | Qty |price | Qty Available to fill order
+--------------+--------+-----+-------+
| 2010-01-01 | 1 | 3 | 1000 | 13 - 3 = 10 avail next order
| 2010-01-02 | 1 | 5 | 1200 | 10 - 5 = 5 avail next order
| 2010-01-03 | 1 | 3 | 1300 | 5 - 3 = 2 avail next order
| 2010-01-03 | 1 | 4 | 1400 | only 2 to PARTIALLY fill this order
| 2010-01-03 | 1 | 7 | 1700 | none available
| 2010-01-08 | 1 | 1 | 1100 | none available
With the extra sample record removed, would result in...
| date | pr_id | Qty |price | Qty Available to fill order
+--------------+--------+-----+-------+
| 2010-01-01 | 1 | 3 | 1000 | 13 - 3 = 10 avail next order
| 2010-01-02 | 1 | 5 | 1200 | 10 - 5 = 5 avail next order
| 2010-01-03 | 1 | 4 | 1400 | 5 - 4 = 1 avail for next order
| 2010-01-03 | 1 | 7 | 1700 | only 1 of the 7 available
| 2010-01-08 | 1 | 1 | 1100 | no more available...
So Aliasghar, does this better represent what you are trying to do??? Fill the available orders based on which order was entered into the system first, fill as many as possible based on inventory and stop there?
Please confirm by adding comment to this answer and maybe we can help resolve... Also, confirm WHICH Database are you using... SQL-Server, Oracle, MySQL, etc...
Here a working query for pr_id=1 , I used MySql:
select final.pr_date, final.pr_id, count(t_qty) as qty, final.price from
(select * FROM (select q.pr_date, q.pr_id, 1 as t_qty, q.price , #t := #t + t_qty total
FROM(
SELECT d.pr_date, d.pr_id, 1 as t_qty, d.price
FROM detail_table d
JOIN generator_4k i
ON i.n between 1 and d.qty
WHERE d.pr_id= 1
Order by d.id, d.pr_date) q
CROSS JOIN (SELECT #t := 0) i) c
WHERE c.total <= (select remain_qty from master_table WHERE product_id = 1)) final
group by final.pr_date , final.pr_id , final.price ;
Here SQL FIDDLE
You have to adapt your detail_table to add a technical id as primary key and create some views, I renamed the date column as pr_date, You'll find the schema on the sql fiddle.
Here another query Using SQL SERVER
select final.pr_date, final.pr_id, count(t_qty) as qty, final.price from
(SELECT top(select remain_qty from master_table WHERE product_id = 1) d.pr_date, d.pr_id, 1 as t_qty, d.price
FROM detail_table d
JOIN generator_4k i
ON i.n between 1 and d.qty
WHERE d.pr_id= 1
Order by d.id, d.pr_date) final
group by final.pr_date , final.pr_id , final.price ;
Here SQL FIDDLE
Daywise product by info
Beneath a suggested statement.
select t2.date,t2.pr_id,t1.pr_name,sum(qty) as qty_buy,sum(price) as amount from master_table as t1
inner join detail_table as t2 on t1.product_id=t2.pr_id
group by t2.date,t2.pr_id
order by t1.date,t2.pr_id
I had a hard time to understand what you really wanted.
So if I understood well, you want some data that correspond to a product but do not go over your remained item.
So I coudn't bypass yet the first query that goes over, and only take the remaining from it.
So my query for now just stop until it s get to the remained items allowed
SQL FIDDLE
To be able to do what you want, you need to first create a view that generate row based on your quantity.
Like something like
> +--------------+------------------+----------+--------+
| date | pr_id | Qty |price |
+--------------+------------------+----------+--------+
| 2010-01-01 | 1 | 3 | 1000 |
turn into something like
> +--------------+------------------+----------+--------+
| date | pr_id | Qty |price |
+--------------+------------------+----------+--------+
| 2010-01-01 | 1 | 1 | 1000
| 2010-01-01 | 1 | 1 | 1000 |
| 2010-01-01 | 1 | 1 | 1000 |
Then you count your rows until your remained item allows you to do it.
After you regroup all of the row by price,pr_id and date.
VOILA

Query to find the first date after a specific grouped sum value

I have an article table that holds the current stock for each article. I need to know the last date when new stock has arrived, after running out of stock for that specific article.
The table looks like this.
+-----------+-----------------+-------+
| ArticleID | StockDate | Stock |
+-----------+-----------------+-------+
| 1 | 1/1/2012 10:15 | 100 |
| 1 | 2/1/2012 13:39 | -50 |
| 1 | 2/1/2012 15:17 | -50 |
| 1 | 4/1/2012 08:05 | 100 |
| 2 | 5/1/2012 09:48 | 50 |
| 1 | 6/1/2012 14:21 | -25 |
| 1 | 7/1/2012 16:01 | 10 |
| 2 | 8/1/2012 13:42 | -10 |
| 1 | 9/1/2012 09:56 | -85 |
| 1 | 13/1/2012 08:12 | 100 |
| 1 | 13/1/2012 10:50 | -15 |
+-----------+-----------------+-------+
The output should look like this.
+-----------+-----------------+
| ArticleID | StockDate |
+-----------+-----------------+
| 2 | 5/1/2012 09:48 |
| 1 | 13/1/2012 08:12 |
+-----------+-----------------+
How did i get this output? ArticleID 1 had a 100 in stock but reached 0 for the first time on 2/1/2012 15:17. Then new stock arrived and it hit 0 again at 9/1/2012 09:56. So the result should shows the first date after the last empty stock grouped by ArticleID. ArticleID 2 didn't had a 0 point, so the first stock date is shown.
I need a result set that can be joined with other queries. So a Stored Procedure does not work for me.
select ArticleID,stockdate from
(
select t.ArticleID, t.stockdate, ROW_NUMBER() Over (partition by t.articleid order by v.articleid desc, stockdate) rn
from yourtable t
left join
(
select ArticleID, MAX(stockdate) as msd from yourtable t1
cross apply (select sum(stock) as stockrt from yourtable where stockdate<=t1.stockdate and ArticleID=t1.ArticleID) rt
where stockrt = 0
group by articleid
) v
on t.ArticleID = v.ArticleID
and t.stockdate>v.msd
) v
where rn=1