SQL query to show most recent qty, but grouped by customer [duplicate] - sql

This question already has answers here:
Oracle SQL query: Retrieve latest values per group based on time [duplicate]
(2 answers)
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Select First Row of Every Group in sql [duplicate]
(2 answers)
Closed 4 years ago.
Item Number | Customer | Creation Date | Onhand Qty
123 1 03-FEB-19 654
234 3 03-FEB-19 987
789 5 03-FEB-19 874
321 4 03-FEB-19 147
567 7 03-FEB-19 632
123 1 29-JAN-19 547
234 3 29-JAN-19 814
789 5 29-JAN-19 458
321 4 29-JAN-19 330
567 7 29-JAN-19 118
I have this data set above, but for thousands of items and hundreds of customers.
What I'd like to do is to just return the latest 'Onhand Qty' field, so max(creation_date) but by item and customer.
Item Number | Customer | Creation Date | Onhand Qty
123 1 03-FEB-19 654
234 3 03-FEB-19 987
789 5 03-FEB-19 874
321 4 03-FEB-19 147
567 7 03-FEB-19 632
Effectively, I'm trying to find the most recent onhand qty amount, by customer and item, so I can say that at the most recent check, 'Customer 1 had 654 units of Item 123'.
Is someone able to help me?
This is in an Oracle database (V11).
Many thanks

Use ROW_NUMBER() as follows :
SELECT * FROM (
SELECT t.*, ROW_NUMBER() OVER(PARTITION BY Customer, Item_Number ORDER BY creation_date DESC) rn
FROM mytable t
) WHERE rn = 1
In the subquery, ROW_NUMBER() assigns a sequence number to each record in groups of records having the same Customer/Item. The sequence is ordered by descending creation date (so the highest date comes first). Then, the outer query filters on the first record in each group.
This DB Fiddle demo with your sample data returns :
ITEM_NUMBER | CUSTOMER | CREATION_DATE | ONHAND_QTY | RN
----------: | -------: | :------------ | ---------: | -:
123 | 1 | 29-JAN-19 | 547 | 1
234 | 3 | 29-JAN-19 | 814 | 1
321 | 4 | 29-JAN-19 | 330 | 1
789 | 5 | 29-JAN-19 | 458 | 1
567 | 7 | 29-JAN-19 | 118 | 1

use row_number()
select * from (select *,row_number() over(partition by Customer order by creation_date desc,qty desc) rn from table
) t where t.rn=1

You can try using row_number() and add partition by Customer,item order by creation_date desc in over clause
select * from
(
select *,row_number() over(partition by Customer,item order by creation_date desc) rn from table
)A where rn=1

Related

SQL: How do I get a filtered count for each distinct row in a table?

Note: this is different from questions that ask "I want a count for each distinct row in a table" which has been answered numerous times. This is a filtered count, so the counting part of the query needs a more complex WHERE clause. Consider this dataset:
customer_id | user_id | age
-----------------------------
1 | 932 | 20
1 | 21 | 3
1 | 2334 | 32
2 | 232 | 10
2 | 238 | 28
3 | 838 | 39
3 | 928 | 83
4 | 842 | 12
I want to query this table and know the number of users over the age of 13 for each distinct customer_id. So the result would be:
customer_id | over_13_count
-----------------------------
1 | 2
2 | 1
3 | 2
4 | 0
I've tried something like this but it just runs forever, so I think I'm doing it wrong:
SELECT DISTINCT customer_id,
(SELECT COUNT(*) FROM mytable AS m2 WHERE m2.customer_id = m1.customer_id AND age > 13) AS over_13_count
FROM mytable AS m1
ORDER BY customer_id
Just use conditional aggregation:
SELECT customer_id,
SUM(CASE WHEN age > 13 THEN 1 ELSE 0 END) asover_13_count
FROM mytable m1
GROUP BY customer_id

For a given table give out the count for each row Oracle SQL

if i have a table like this:
ID | Name | Payment
----------------------
101 | Victor | 10
103 | Andy | 13
134 | Mai | 2
156 | Chris | 68
179 | Ryan | 43
And I wanna have a query that gives out the following
[Count] | ID | Name | Payment
----------------------
1 | 101 | Victor | 10
2 | 103 | Andy | 13
3 | 134 | Mai | 2
4 | 156 | Chris | 68
5 | 179 | Ryan | 43
So it gives out the number of each row but I don't know how to do it ( am a beginner at SQL). Any tips?
Simply use row_number():
select row_number() over (order by id) as "Count",
t.*
from t
order by "Count";
You should have both order bys to be sure the numbering on the rows is correct and the order of the rows in the result set is correct.
use dense_rank()
select *,dense_rank() over(order by id) from t
or you could use count()
select *,count(*) over(order by id) from t
simple
select rownum, ID , Name, Payment from table

Order rows by ntile and row_number

I'm trying to build stored procedure that will return data for Crystal Reports report.
Inside CR I'm using multi column layout.
I want to get 3 layout column something like this:
1 5 8
2 6 9
3 7 10
4
But because CR has some layout issues it is ordering my table like this:
1 2 3
4 5 6
7 8 9
10
So I've tried to create procedure that will return extra column on which I'll sort my data.
So instead 1,2,3,4 order I need 1,4,7,10,2,5,8,3,6,9...
I have table with that data:
ID | CASE_ID | CASE_DATE
--------------------------
1 | 1 | 2014-02-03
2 | 1 | 2014-02-04
3 | 1 | 2014-02-05
4 | 1 | 2014-02-06
5 | 1 | 2014-02-07
6 | 1 | 2014-02-08
7 | 1 | 2014-02-09
8 | 1 | 2014-02-10
9 | 1 | 2014-02-11
10 | 1 | 2014-02-12
AND I need stored procedure that will return this data:
ID | CASE_ID | CASE_DATE | ORDER
---------------------------------
1 | 1 | 2014-02-03 | 1
2 | 1 | 2014-02-04 | 5
3 | 1 | 2014-02-05 | 8
4 | 1 | 2014-02-06 | 2
5 | 1 | 2014-02-07 | 6
6 | 1 | 2014-02-08 | 9
7 | 1 | 2014-02-09 | 3
8 | 1 | 2014-02-10 | 7
9 | 1 | 2014-02-11 | 10
10 | 1 | 2014-02-12 | 4
Here is sql fiddle with sample data and my code: http://sqlfiddle.com/#!3/c24c1/1
Idea behind sort column:
divide all rows into 3 groups (ntile), take first item from first group, then first from second and first from third group
EDIT:
Here is my temporary solution, I hope that running this will clarify what I had in mind when I was asking this question:
--DECLARE #NUM INT;
--SET #NUM=3;
SELECT ID,
CASE_ID,
CONVERT(NVARCHAR(10),CASE_DATE,121) AS DATA,
(ROW1 - 1) * 3/*#NUM*/ + COL AS [ORDER]
FROM
( SELECT CASE_ID,
ID,
ROW AS LP,
COL,
ROW_NUMBER() OVER (PARTITION BY CASE_ID, COL ORDER BY ROW) AS ROW1,
CASE_DATE
FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY D.CASE_ID ORDER BY D.ID) AS ROW,
NTILE(3/*#NUM*/) OVER (PARTITION BY D.CASE_ID ORDER BY D.ID) AS COL,
ID,
D.CASE_ID,
CASE_DATE
FROM DATA D
WHERE D.CASE_ID = 1)X )Y
ORDER BY Y.CASE_ID,
LP
Edit: It looks like you actually want the ORDER column, not just returning the columns in that order.
SELECT ID,
CASE_ID,
DATA,
ROW_NUMBER() OVER (ORDER BY ROW, N) AS [ORDER]
FROM (
SELECT ID,
CASE_ID,
N,
ROW_NUMBER() OVER (PARTITION BY CASE_ID, N ORDER BY ID) AS ROW,
DATA
FROM (
SELECT
ID,
CASE_ID,
NTILE(3) OVER (PARTITION BY CASE_ID ORDER BY ID) AS N,
CONVERT(NVARCHAR(10), CASE_DATE,121) AS DATA
FROM DATA
WHERE CASE_ID = 1 ) X ) Y
ORDER BY ID;
SQLFiddle

Postgresql incremental and conditional filtering

I have this table
ID value user stock
----|--------|---------|---------
1 | 10 | mark | AAPL
2 | 20 | rob | GOOG
3 | 30 | mark | AAPL
4 | -40 | mark | AAPL
5 | -10 | rob | GOOG
6 | 25 | mark | GOOG
7 | 5 | mark | GOOG
8 | 45 | mark | AAPL
I would like to build a query (possibly without using any PGSQL function) that returns the rows shown below. It should start in order (ID ASC) summing "value" column grouped by user,stock. If the temporary sum is 0, all the previous rows (for that group) will be discarded.
id value user stock
----|--------|---------|---------
2 | 20 | rob | GOOG
5 | -10 | rob | GOOG
6 | 25 | mark | GOOG
7 | 5 | mark | GOOG
8 | 45 | mark | AAPL
I think that OVER (PARTITION BY) and WINDOW function should be used
SELECT *, SUM(value) OVER w AS scm
FROM "mytable"
WINDOW w AS (PARTITION BY user,stock ORDER BY id ASC)
this returns next table
ID value user stock scm
----|--------|---------|---------|-------
1 | 10 | mark | AAPL | 10
2 | 20 | rob | GOOG | 20
3 | 30 | mark | AAPL | 40
4 | -40 | mark | AAPL | 0
5 | -10 | rob | GOOG | 10
6 | 25 | mark | GOOG | 25
7 | 5 | mark | GOOG | 30
8 | 45 | mark | AAPL | 45
So this should be a good starting point, because it shows that APPL for mark is 0 (id=4) and for that group (AAPL,mark) I should keep all the following rows.
The rule is: for each group (stock,user) keep all the rows following the last row with scm=0
SQL Fiddle
with s as (
select *,
count(scm = 0 or null) over w z
from (
select *,
sum(value) over w as scm
from mytable
window w as (partition by "user", stock order by id asc)
) s
window w as (partition by "user", stock order by id asc)
)
select *
from
s
inner join
(
select max(z) z, "user", stock
from s
group by "user", stock
) z using (z, "user", stock)
where scm > 0
order by s.user, s.stock, id
Something like the following I think would get you want you want. Basically it will perform the following:
Use the SQL statement you have to compute the cumulative sums.
Compute the minimum ID that should be displayed for each (username, stock) group.
Select from the original SQL cumulative sum and filter out any IDs lower than the minimum ID.
WITH sums AS (
SELECT id, value, username, stock, SUM(value) OVER w AS scm
FROM "mytable"
WINDOW w AS (PARTITION BY user,stock ORDER BY id ASC)),
minimum_ids AS (
SELECT username, stock, MAX(id) as minimum_id
FROM sums
WHERE scm <= 0
GROUP BY username, stock)
SELECT sums.id, sums.value, sums.username, sums.stock, sums.scm
FROM sums
LEFT JOIN minimum_ids
ON (sums.username = minimum_ids.username
AND sums.stock = minimum_ids.stock)
WHERE (minimum_ids.minimum_id IS NULL OR sums.id > minimum_ids.minimum_id)
ORDER BY id;

Compare multiple columns in SQL Server returning largest version by product

Using SQL Server 2005, I am attempting to write a select statement which will query the data below (18000 rows) and return one row per Product, finding the largest product version.
I think the query needs to compare the versions in reverse, and storing the largest row number as it gets up to Major but I am not sure.
Sample Data
Product Date Major Minor Release Fixpack Build
======= ========== ====== ====== ====== ====== ======
Widgets 01/01/2012 10 1 0 345 132
Widgets 04/02/2011 6 6 3 932 245
Widgets 09/01/2010 4 92 345 53 2
Gears 03/17/2011 9 2 3 0 21
Gears 12/12/2012 10 0 0 5 548
Tires 04/24/2000 8 3 456 3 984
Tires 04/12/1993 7 14 45 35 252
Expected Results
Product Date Major Minor Release Fixpack Build
======= ========== ====== ====== ====== ====== ======
Widgets 01/01/2012 10 1 0 345 132
Gears 12/12/2012 10 0 0 5 548
Tires 04/24/2000 8 3 456 3 984
OR even better, could combine the versions into one column:
Product Date ProductVersion
======= ========== ==================
Widgets 01/01/2012 10.1.0.345.132
Gears 12/12/2012 10.0.0.5.548
Tires 04/24/2000 8.3.456.3.984
This should work:
SELECT a.product,
a.md,
b.major,
b.minor,
b.release,
b.fixpack,
b.build
FROM (SELECT product,
Max(date) AS md
FROM tbl
GROUP BY product) a
JOIN tbl b
ON b.product = a.product
AND b.date = a.md
Result
| PRODUCT | MD | MAJOR | MINOR | RELEASE | FIXPACK | BUILD |
--------------------------------------------------------------------
| Widgets | 2012-01-01 | 10 | 1 | 0 | 345 | 132 |
| Tires | 2000-04-24 | 8 | 3 | 456 | 3 | 984 |
| Gears | 2012-12-12 | 10 | 0 | 0 | 5 | 548 |
Even better method
SELECT a.product,
a.md AS date,
Cast(b.major AS VARCHAR(5)) + '.'
+ Cast(b.minor AS VARCHAR(5)) + '.'
+ Cast(b.release AS VARCHAR(5)) + '.'
+ Cast(b.fixpack AS VARCHAR(5)) + '.'
+ Cast(b.build AS VARCHAR(5)) AS version
FROM (SELECT product,
Max(date) AS md
FROM tbl
GROUP BY product) a
JOIN tbl b
ON b.product = a.product
AND b.date = a.md
| PRODUCT | DATE | VERSION |
-----------------------------------------
| Widgets | 2012-01-01 | 10.1.0.345.132 |
| Tires | 2000-04-24 | 8.3.456.3.984 |
| Gears | 2012-12-12 | 10.0.0.5.548 |
You can use the ROW_NUMBER function in a derived table. Do the partition on Product and order by Date desc or your version information.
Ordered by Date desc:
select T.Product,
T.Date,
T.ProductVersion
from (
select Product,
Date,
cast(Major as varchar(5))+'.'+
cast(Minor as varchar(5))+'.'+
cast(Release as varchar(5))+'.'+
cast(Fixpack as varchar(5))+'.'+
cast(Build as varchar(5)) as ProductVersion,
row_number() over(partition by Product order by Date desc) as rn
from YourTable
) as T
where T.rn = 1
Ordered by ProductVersion desc
select T.Product,
T.Date,
T.ProductVersion
from (
select Product,
Date,
cast(Major as varchar(5))+'.'+
cast(Minor as varchar(5))+'.'+
cast(Release as varchar(5))+'.'+
cast(Fixpack as varchar(5))+'.'+
cast(Build as varchar(5)) as ProductVersion,
row_number() over(partition by Product
order by Major desc,
Minor desc,
Release desc,
Fixpack desc,
Build desc) as rn
from YourTable
) as T
where T.rn = 1
SQL Fiddle