Select MAX issue SQL - sql

Scenario:
I have a table with order status, for example:
/ ORDER LOG NUMBER LOG CODE
1 1 1 Preparing
2 1 2 Prepared
3 1 3 Sent
4 2 1 Preparing
5 2 2 Prepared
6 3 1 Preparing
I've been looking for a way to select orders, where last log code is Prepared.
For example I want to see all ORDERS where last LOG CODE is Prepared (last log)

Oracle supports Windowed Aggregates:
select *
from
( select
ORDER
,LOG_NUMBER
,LOG_CODE
-- last log number for each order
,max(LOG_NUMBER) over (partition by ORDER) as maxnum
from mytable
) dt
where LOG_NUMBER = maxnum
and LOG_CODE = 'Prepared'
or
select *
from
( select
ORDER
,LOG_NUMBER
,LOG_CODE
-- youngest row gets row_number 1
,ROW_NUMBER() over (partition by ORDER order by LOG_NUMBER desc) as rn
from mytable
) dt
where rn = 1
and LOG_CODE = 'Prepared'

You can use the analytic function to do so
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions070.htm
It should be something like:
SELECT order LAG(log_code, 1, 0) OVER (PARTITION BY order ORDER BY log_number) AS prev_code FROM orders
This will at least deliver you a resultset which has the last code information.
Instead of using an outer select you should be able to extend the query with
"having prev_code = 'Prepared'"

A pretty efficient way is to use correlated subqueries:
select t.*
from t
where t.lognumber = (select max(t2.lognumber) from t t2 where t.order = t2.order) and
t.logcode = 'Prepared';

Related

Return second from the last oracle sql

SELECT * FROM
(
SELECT DISTINCT(TRUNC(receipt_dstamp))
FROM inventory
WHERE substr(location_id,1,3) = 'GI-'
ORDER BY 1 ASC
)
WHERE ROWNUM <= 5
Output:
Hi all, i've got this subeqery and in this case my oldest date is in row 1, i want to retrive only second from the last(from the top in this case) which is gonna be 01-SEP-21.
I was trying to play with ROWNUM and OVER but without any results, im getting blank output.
Thank you.
Full query:
SELECT TRUNC(receipt_dstamp) as old_putaway_date, COUNT(tag_id) as tag_old_putaway
FROM inventory
WHERE substr(location_id,1,3) = 'GI-'
AND TRUNC(receipt_dstamp) IN (
SELECT * FROM
(
SELECT DISTINCT(TRUNC(receipt_dstamp))
FROM inventory
WHERE substr(location_id,1,3) = 'GI-'
ORDER BY 1 ASC
)
WHERE ROWNUM = 1
)
GROUP BY TRUNC(receipt_dstamp);
You should be able to simplify the entire query to:
SELECT old_putaway_date,
COUNT(tag_id) as tag_old_putaway
FROM (
SELECT TRUNC(receipt_dstamp) as old_putaway_date,
tag_id,
DENSE_RANK() OVER (ORDER BY TRUNC(receipt_dstamp)) AS rnk
FROM inventory
WHERE substr(location_id,1,3) = 'GI-'
)
WHERE rnk = 3
GROUP BY
old_putaway_date;
You can use dense_rank() :
SELECT * FROM (
SELECT L.*,DENSE_RANK()
OVER (PARTITION BY L.TAG_OLD_PUTAWAY ORDER BY L.OLD_PUTAWAY_DATE DESC) RNK
FROM
(
SELECT TRUNC(receipt_dstamp) as old_putaway_date, COUNT(tag_id) as tag_old_putaway
FROM inventory
WHERE substr(location_id,1,3) = 'GI-'
AND TRUNC(receipt_dstamp) IN (
SELECT * FROM
(
SELECT DISTINCT(TRUNC(receipt_dstamp))
FROM inventory
WHERE substr(location_id,1,3) = 'GI-'
ORDER BY 1 ASC
)
WHERE ROWNUM = 1
)
GROUP BY TRUNC(receipt_dstamp)
) L
) WHERE RNK = 2
You are using an old Oracle syntax that is not standard compliant in the regard that it relies on a subquery result order. (Sub)query results are unordered data sets by definition, but Oracle lets this pass in order to make their ROWNUM work with it.
Oracle now supports the standard SQL FETCH clause, which you should use instead.
SELECT DISTINCT TRUNC(receipt_dstamp) AS receipt_date
FROM inventory
WHERE SUBSTR(location_id, 1, 3) = 'GI-'
ORDER BY receipt_date
OFFSET 2 ROWS
FETCH NEXT 1 ROW ONLY;
https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html#GUID-CFA006CA-6FF1-4972-821E-6996142A51C6

Exclude records where count > 5 and select top 1 of it

I want to exclude records where id > 5 then select the top 1 of it order by date. How can I achieve this? Each record has audit_line which is unique field for each record. Recent SQL script is on below:
SELECT *
FROM db.table
HAVING COUNT(id) > 5
If you want id > 5 then you want where:
select top (1) t.*
from db.table t
where id > 5
order by date;
You can use row-numbering for this.
Note that if you have no other column to order by, you can do ORDER BY (SELECT NULL), but then you may get different results on each run.
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY some_other_column) rn
FROM table
) t
WHERE rn = 5;

Selecting a column where another column is maximal

I guess this is a standard problem. But I could not find a proper solution yet.
I have three columns in table A:
ID ID_Version Var
1 1 A
1 2 A
1 3 X
1 4 D
2 1 B
2 2 Z
2 3 D
3 1 A
4 1 B
4 2 Q
4 3 Z
For every unique ID, I would like to isolate the Var-value that belongs to the maximal ID-Version.
For ID = 1 this would be D, for ID = 2 this would be D, for ID = 3 this would be A and for ID = 4 this would be Z.
I tried to use a group by statement but I cannot select Var-values when using the max-function on ID-Version and grouping by ID.
Does anyone have a clue how to write fast, effective code for this simple problem?
use row_number() analytic function :
select ID,Var from
(
select row_number() over (partition by id order by id_version desc) as rn,
t.*
from tab t
)
where rn = 1
or max(var) keep (dense_rank...)
select id, max(var) keep (dense_rank first order by id_version desc) as var
from tab
group by id
Demo
You could use ranking function:
SELECT *
FROM (SELECT tab.*, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY ID_Version DESC) rn
FROM tab)
WHERE rn = 1
Oracle has the keep syntax, so you can also use aggregation:
select id, max(id_version) as id_version,
max(var) keep (dense_rank first order by id_version desc) as var
from a
group by id;
You could also use a simple join to do what you want, see below :
SELECT A.id, A.var FROM A
JOIN
(SELECT id, MAX(id_version) as id_version
FROM A
GROUP BY id) temp ON (temp.id = A.id AND temp.id_version = A.id_version)
Or you could also use a subquery like this :
SELECT a1.id, a1.var FROM A a1
WHERE a1.id_version = (SELECT MAX(id_version) FROM A a2 WHERE a2.id = a1.id)

Subquery with eloquent model in laravel 5

I have the following raw query:
select *
from (select * from settings order by priority desc) x
group by name
I would like to use a laravel 5 query builder instead but cant get the subquery to work. Is there a way to do this without using raw query's?
The table:
id - name - priority
1 1 1
2 1 2
3 2 1
In the above case It will return row 2 and 3 like I want to
I tried this query:
$settings = \App\Setting::where('user_id', Auth::id())->orWhere('tool_id', $id)->groupBy('name')->orderBy('priority', 'desc')->get();
But this will return row 1 and 3.
Can you use CTE expressions? RANK() would work:
WITH CTE AS
(
SELECT *, RANK() OVER(PARTITION BY name ORDER BY priority desc) AS RowRank
FROM settings
)
SELECT id, name, priority
FROM CTE c
WHERE c.RowRank = 1

Getting all fields from table filtered by MAX(Column1)

I have table with some data, for example
ID Specified TIN Value
----------------------
1 0 tin1 45
2 1 tin1 34
3 0 tin2 23
4 3 tin2 47
5 3 tin2 12
I need to get rows with all fields by MAX(Specified) column. And if I have few row with MAX column (in example ID 4 and 5) i must take last one (with ID 5)
finally the result must be
ID Specified TIN Value
-----------------------
2 1 tin1 34
5 3 tin2 12
This will give the desired result with using window function:
;with cte as(select *, row_number(partition by tin order by specified desc, id desc) as rn
from tablename)
select * from cte where rn = 1
Edit: Updated query after question edit.
Here is the fiddle
http://sqlfiddle.com/#!9/20e1b/1/0
SELECT * FROM TBL WHERE ID IN (
SELECT max(id) FROM
TBL WHERE SPECIFIED IN
(SELECT MAX(SPECIFIED) FROM TBL
GROUP BY TIN)
group by specified)
I am sure we can simplify it further, but this will work.
select * from tbl where id =(
SELECT MAX(ID) FROM
tbl where specified =(SELECT MAX(SPECIFIED) FROM tbl))
One method is to use window functions, row_number():
select t.*
from (select t.*, row_number() over (partition by tim
order by specified desc, id desc
) as seqnum
from t
) t
where seqnum = 1;
However, if you have an index on tin, specified id and on id, the most efficient method is:
select t.*
from t
where t.id = (select top 1 t2.id
from t t2
where t2.tin = t.tin
order by t2.specified desc, id desc
);
The reason this is better is that the index will be used for the subquery. Then the index will be used for the outer query as well. This is highly efficient. Although the index will be used for the window functions; the resulting execution plan probably requires scanning the entire table.