Selecting a column where another column is maximal - sql

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)

Related

Unable to get dedupe records with rank

I am trying to dedupe my dataset using rank, but it is not assigning a different number to the second record. What am I doing wrong here?
with get_rank as (
select id, code, rank() over (partition by id order by z.rowid) as ranking
from mytable z
)
select *
from get_rank
where ranking = 1
and id = 72755
ID CODE RANKING
---------- ---- ----------
72755 M 1
72755 M 1
Use row_number():
with get_rank as (
select id, code,
row_number() over (partition by id order by z.rowid) as ranking
from mytable z
)
select *
from get_rank
where ranking = 1 and id = 72755;
It is guaranteed to return a different value for each row.

Select MAX issue 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';

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.

How to get distinct record from two tables in sql?

i have two table showin below
news (table name)
NewsId NewsDate
1 25-03-2014
2 29-03-2014
newsImageGal (table name)
newsSr newsId newsImages
1 1 images/i.jpg
2 1 images/j.jpg
3 1 images/k.jpg
4 2 images/l.jpg
5 2 images/m.jpg
6 2 images/n.jpg
i want a result like
NewsId NewsDate newsId newsImages
1 25-03-2014 1 images/i.jpg
2 9-03-2014 2 images/l.jpg
i have tried using join query and group by function but it shows duplicate multiple records how to solve this query?
You can write as
;WITH CTE AS
( SELECT N.NewsId
,N.NewsDate
,NIG.newsImages
,ROW_NUMBER() OVER (PARTITION BY N.NewsId ORDER BY NIG.newsSr ASC)
AS rownum
FROM news N
JOIN newsImageGal NIG ON N.NewsId = NIG.newsId
)
SELECT NewsId,
NewsDate,
newsImages
FROM CTE
WHERE rownum = 1
Try this.
SELECT a.NewsId,
a.NewsDate,
b.newsImages
FROM news a
JOIN (SELECT Row_number()OVER(partition BY newsid ORDER BY newssr) rn,
*
FROM newsImageGal) b
ON a.NewsId = b.newsId
WHERE rn = 1
Key here is to find the first image that got added into newsImageGal table per newsid.
so use the window function to create Row_number per newsid in order of newsSr.
SELECT Row_number()OVER(partition BY newsid ORDER BY newssr) rn,
*
FROM newsImageGal
From the above query you can see the rn=1 is the first image that got added into the newsImageGal join the result with news table and filter the result with rn=1
can you try this
select distinct ng.newsId, ng.newsImages
from (
select MIN(newsSr),newsId,newsImages
from newsImageGal
group by newsId,newsImages) ng
join news ne
on ne.newsId=ng.newsId
select n.NewsId,n.NewsDate,g.newsImages
from news n
join newsImageGal g on n.NewsId=g.newsSr
SELECT n.NewsId,
n.NewsDate,
ng.newsId,
ng.newsimages
FROM news n,
(SELECT *,
Rank()OVER(PARTITION BY newsId ORDER BY newsSr DESC) AS rank
FROM newsImageGal) ng
WHERE n.NewsId = ng.newsId
AND rank = 1

Tsql to get first random product in a category

I've this result set:
select a.id, a.categoria from Articolo a
where novita = 1
order by a.categoria, newid()
id categoria
----------- -----------
3 4
11 4
1 4
12 5
13 5
4 6
and i would to get the first product (in a random order) from each different category:
id categoria
----------- -----------
3 4
12 5
4 6
Ideally something like
select FIRST(a.id), a.categoria from Articolo a
where novita = 1
order by a.categoria, newid()
Any ideas?
Use MAX(a.id) with GROUP BY a.categoria
SELECT MAX(a.id), a.categoria
from Articolo a
where novita = 1
GROUP BY a.category
Update
To get random id for each categoria you can use the ranking function ROW_NUMBER() OVER(PARTITION BY categoria) with ORDER BY NEWID to get a random ordering, like this:
WITH CTE
AS
(
SELECT id, categoria, ROW_NUMBER() OVER(PARTITION BY categoria
ORDER BY NEwID()) AS rn
FROM Articolo
)
SELECT id, categoria
FROM CTE
WHERE rn = 1;
See it in action here:
SQL Fiddle Demo
This way, it will give you a random id for each categoria each time.
However, If you want the first, you can use the ORDER BY(SELECT 1) inside the ranking function ROW_NUMBER():
WITH CTE
AS
(
SELECT id, categoria, ROW_NUMBER() OVER(PARTITION BY categoria
ORDER BY (select 1)) AS rn
FROM Articolo
)
SELECT id, categoria
FROM CTE
WHERE rn = 1;
Updated SQL Fiddle Demo
This will give you the first id for each categoria.
Note that: There is no meaning of the first value in the database concepts, because in the relational model, the rows order is not significant. And it is not guaranteed to return the same order each time, you have to ORDER BY specific column to get consistent ordering.