SQL value of previous (unknown) date - sql

I have a table with articles, Day Date, and amount of bought. I want a reslut table where I can see the amount off all Articles and how many where bought and the amount they where bought at the unknown date before:
Example
result of:
select articleid, amount, date from table1 where articleid in(7,8)
|------------|---------|----------|
| articleid | amount | date |
|------------|---------|----------|
| 7 | 34 |20.10.2019|
|------------|---------|----------|
| 7 | 2 |15.10.2019|
|------------|---------|----------|
| 8 | 12 |13.10.2019|
|------------|---------|----------|
| 8 | 35 |15.09.2019|
|------------|---------|----------|
The result should look like:
|------------|---------|----------|----------|----------|
| articleid | amount | date |prev date |prevamount|
|------------|---------|----------|----------|----------|
| 7 | 34 |20.10.2019|15.10.2019| 2 |
|------------|---------|----------|----------|----------|
| 7 | 2 |15.10.2019| | |
|------------|---------|----------|----------|----------|
| 8 | 12 |13.10.2019|15.09.2019| 35 |
|------------|---------|----------|----------|----------|
| 8 | 35 |15.09.2019| | |
|------------|---------|----------|----------|----------|
Is this anyway possibile to do?
Best
Zio

You want lag():
select
articleid,
amount,
date,
lag(date) over(partition by articleid order by date) prevdate,
lag(amount) over(partition by articleid order by date) prevamount
from table1
order by articleid, date desc

Related

Find the first order of a supplier in a day using SQL

I am trying to write a query to return supplier ID (sup_id), order date and the order ID of the first order (based on earliest time).
+--------+--------+------------+--------+-----------------+
|orderid | sup_id | items | sales | order_ts |
+--------+--------+------------+--------+-----------------+
|1111132 | 3 | 1 | 27,0 | 24/04/17 13:00 |
|1111137 | 3 | 2 | 69,0 | 02/02/17 16:30 |
|1111147 | 1 | 1 | 87,0 | 25/04/17 08:25 |
|1111153 | 1 | 3 | 82,0 | 05/11/17 10:30 |
|1111155 | 2 | 1 | 29,0 | 03/07/17 02:30 |
|1111160 | 2 | 2 | 44,0 | 30/01/17 20:45 |
|....... | ... | ... | ... | ... ... |
+--------+--------+------------+--------+-----------------+
Output I am looking for:
+--------+--------+------------+
| sup_id | date | order_id |
+--------+--------+------------+
|....... | ... | ... |
+--------+--------+------------+
I tried using a subquery in the join clause as below but didn't know how to join it without having selected order_id.
SELECT sup_id, date(order_ts), order_id
FROM sales s
JOIN
(
SELECT sup_id, date(order_ts) as date, min(time(order_date))
FROM sales
GROUP BY merchant_id, date
) m
on ...
Kindly assist.
You can use not exists:
select *
from sales
where not exists (
-- find sales for same supplier, earlier date, same day
select *
from sales as older
where older.sup_id = sales.sup_id
and older.order_ts < sales.order_ts
and older.order_ts >= cast(sales.order_ts as date)
)
The query below might not be the fastest in the world, but it should give you all information you need.
select order_id, sup_id, items, sales, order_ts
from sales s
where order_ts <= (
select min(order_ts)
from sales m
where m.sup_id = s.sup_id
)
select sup_id, min(order_ts), min(order_id) from sales
where order_ts = '2022-15-03'
group by sup_id
Assumed orderid is an identity / auto increment column

Subtracting previous row value from current row

I'm doing an aggregation like this:
select
date,
product,
count(*) as cnt
from
t1
where
yyyy_mm_dd in ('2020-03-31', '2020-07-31', '2020-09-30', '2020-12-31')
group by
1,2
order by
product asc, date asc
This produces data which looks like this:
| date | product | cnt | difference |
|------------|---------|------|------------|
| 2020-03-31 | p1 | 100 | null |
| 2020-07-31 | p1 | 1000 | 900 |
| 2020-09-30 | p1 | 900 | -100 |
| 2020-12-31 | p1 | 1100 | 200 |
| 2020-03-31 | p2 | 200 | null |
| 2020-07-31 | p2 | 210 | 10 |
| ... | ... | ... | x |
But without the difference column. How could I make such a calculation? I could pivot the date column and subtract that way but maybe there's a better way
Was able to use lag with partition by and order by to get this to work:
select
date,
product,
count,
count - lag(count) over (partition by product order by date, product) as difference
from(
select
date,
product,
count(*) as count
from
t1
where
yyyy_mm_dd in ('2020-03-31', '2020-07-31', '2020-09-30', '2020-12-31')
group by
1,2
) t

Select pairs of values based on condition in other column - PostgreSQL

I've been trying to solve an issue for the past couple of days, but couldn't figure out what the solution would be...
I have a table as the following:
+--------+-----------+-------+
| ShopID | ArticleID | Price |
+--------+-----------+-------+
| 1 | 3 | 150 |
| 1 | 2 | 80 |
| 3 | 3 | 100 |
| 4 | 2 | 95 |
+--------+-----------+-------+
And I woud like to select pairs of shop IDs for which the price of the same article is higher.
F.e. this should look like:
+----------+----------+---------+
| ShopID_1 | ShopID_2 |ArticleID|
+----------+----------+---------+
| 4 | 1 | 2 |
| 1 | 3 | 3 |
+----------+----------+---------+
... showing that Article 2 ist more expensive in ShopID 4 than in ShopID 2. Etc
My code so far looks as following:
SELECT ShopID AS ShopID_1, ShopID AS ShopID_2, ArticleID FROM table
WHERE table.ArticleID=table.ArticleID and table.Price > table.Price
But it doesn't give the result I am searching for.
Can anyone help me with this objective? Thank you very much.
The problem here is about calculating Top N items per Group.
Assuming you have the following data, in table sales.
# select * from sales;
shopid | articleid | price
--------+-----------+-------
1 | 2 | 80
3 | 3 | 100
4 | 2 | 95
1 | 3 | 150
5 | 3 | 50
With the following query we can create a partition for each ArticleId
select
ArticleID,
ShopID,
Price,
row_number() over (partition by ArticleID order by Price desc) as Price_Rank from sales;
This will result:
articleid | shopid | price | price_rank
-----------+--------+-------+------------
2 | 4 | 95 | 1
2 | 1 | 80 | 2
3 | 1 | 150 | 1
3 | 3 | 100 | 2
3 | 5 | 50 | 3
Then we simply select Top 2 items for each AritcleId:
select
ArticleID,
ShopID,
Price
from (
select
ArticleID,
ShopID,
Price,
row_number() over (partition by ArticleID order by Price desc) as Price_Rank
from sales) sales_rank
where Price_Rank <= 2;
which will result:
articleid | shopid | price
-----------+--------+-------
2 | 4 | 95
2 | 1 | 80
3 | 1 | 150
3 | 3 | 100
Finally, we can use crosstab function to get the expected pivot view.
select *
from crosstab(
'select
ArticleID,
ShopID,
ShopID
from (
select
ArticleID,
ShopID,
Price,
row_number() over (partition by ArticleID order by Price desc) as Price_Rank
from sales) sales_rank
where Price_Rank <= 2')
AS sales_top_2("ArticleID" INT, "ShopID_1" INT, "ShopID_2" INT);
And the result:
ArticleID | ShopID_1 | ShopID_2
-----------+----------+----------
2 | 4 | 1
3 | 1 | 3
Note:
You may need to call CREATE EXTENSION tablefunc; in case if you get the error function crosstab(unknown) does not exist.
This query should work:
SELECT t1.ShopID AS ShopID_1, t2.ShopID AS ShopID_2, t1.ArticleID
FROM <yourtable> t1 JOIN
<yourtable> t2
ON t1.ArticleID = t2.ArticleID AND t1.Price > t2.Price;
That is, you need a self-join and appropriate table aliases.

SQL Query to Find Min and Max Values between Values, dates and companies in the same Query

This is to find the historic max and min price of a stock in the same query for every past 10 days from the current date. below is the data. I've tried the query but getting the same high and low for all the rows. The high and low needs to be calculated per stock for a period of 10 days.
RDBMS -- SQL Server 2014
Note: also duration might be past 30 to 2months if required ie... 30 days. or 60 days.
for example, the output needs to be like ABB,16-12-2019,1480 (MaxClose),1222 (MinClose) (test data) in last 10 days.
+------+------------+-------------+
| Name | Date | Close |
+------+------------+-------------+
| ABB | 26-12-2019 | 1272.15 |
| ABB | 24-12-2019 | 1260.15 |
| ABB | 23-12-2019 | 1261.3 |
| ABB | 20-12-2019 | 1262 |
| ABB | 19-12-2019 | 1476 |
| ABB | 18-12-2019 | 1451.45 |
| ABB | 17-12-2019 | 1474.4 |
| ABB | 16-12-2019 | 1480.4 |
| ABB | 13-12-2019 | 1487.25 |
| ABB | 12-12-2019 | 1484.5 |
| INFY | 26-12-2019 | 73041.66667 |
| INFY | 24-12-2019 | 73038.33333 |
| INFY | 23-12-2019 | 73036.66667 |
| INFY | 20-12-2019 | 73031.66667 |
| INFY | 19-12-2019 | 73030 |
| INFY | 18-12-2019 | 73028.33333 |
| INFY | 17-12-2019 | 73026.66667 |
| INFY | 16-12-2019 | 73025 |
| INFY | 13-12-2019 | 73020 |
| INFY | 12-12-2019 | 73018.33333 |
+------+------------+-------------+
The query I tried but no luck
select max([close]) over (PARTITION BY name) AS MaxClose,
min([close]) over (PARTITION BY name) AS MinClose,
[Date],
name
from historic
where [DATE] between [DATE] -30 and [DATE]
and name='ABB'
group by [Date],
[NAME],
[close]
order by [DATE] desc
If you just want the highest and lowest close per name, then simple aggregation is enough:
select name, max(close) max_close, min(close) min_close
from historic
where close >= dateadd(day, -10, getdate())
group by name
order by name
If you want the entire corresponding records, then rank() is a solution:
select name, date, close
from (
select
h.*,
rank() over(partition by name order by close) rn1,
rank() over(partition by name order by close desc) rn2
from historic h
where close >= dateadd(day, -10, getdate())
) t
where rn1 = 1 or rn2 = 1
order by name, date
Top and bottom ties will show up if any.
You can add a where condition to filter on a given name.
If you are looking for a running min/max
Example
Select *
,MinClose = min([Close]) over (partition by name order by date rows between 10 preceding and current row)
,MaxClose = max([Close]) over (partition by name order by date rows between 10 preceding and current row)
From YourTable
Returns

SQL query to select today and previous day's price

I have historic stock price data that looks like the below. I want to generate a new table that has one row for each ticker with the most recent day's price and its previous day's price. What would be the best way to do this? My database is Postgres.
+---------+------------+------------+
| ticker | price | date |
+---------+------------+------------|
| AAPL | 6 | 10-23-2015 |
| AAPL | 5 | 10-22-2015 |
| AAPL | 4 | 10-21-2015 |
| AXP | 5 | 10-23-2015 |
| AXP | 3 | 10-22-2015 |
| AXP | 5 | 10-21-2015 |
+------- +-------------+------------+
You can do something like this:
with ranking as (
select ticker, price, dt,
rank() over (partition by ticker order by dt desc) as rank
from stocks
)
select * from ranking where rank in (1,2);
Example: http://sqlfiddle.com/#!15/e45ea/3
Results for your example will look like this:
| ticker | price | dt | rank |
|--------|-------|---------------------------|------|
| AAPL | 6 | October, 23 2015 00:00:00 | 1 |
| AAPL | 5 | October, 22 2015 00:00:00 | 2 |
| AXP | 5 | October, 23 2015 00:00:00 | 1 |
| AXP | 3 | October, 22 2015 00:00:00 | 2 |
If your table is large and have performance issues, use a where to restrict the data to last 30 days or so.
Best bet is to use a window function with an aggregated case statement which is used to create a pivot on the data.
You can see more on window functions here: http://www.postgresql.org/docs/current/static/tutorial-window.html
Below is a pseudo code version of where you may need to head to answer your question (sorry I couldn't validate it due to not have a postgres database setup).
Select
ticker,
SUM(CASE WHEN rank = 1 THEN price ELSE 0 END) today,
SUM(CASE WHEN rank = 2 THEN price ELSE 0 END) yesterday
FROM (
SELECT
ticker,
price,
date,
rank() OVER (PARTITION BY ticker ORDER BY date DESC) as rank
FROM your_table) p
WHERE rank in (1,2)
GROUP BY ticker.
Edit - Updated the case statement with an 'else'