SQL get column value associated with max() - sql

I have the following table
ID Version
--- ----------
123 1
124 2
125 3
126 4
127 5
128 6
Now I need to get ID value where version# is maximum
What I can do is
select ID from tbl where version = (select max(version) from tbl)
I don't want to use this since i need to use this part in a join inside another query and i don't want to complicate things further.

You can use select FIRST():
SELECT FIRST(id) FROM tbl ORDER BY Version DESC
Or limit the number results using LIMIT 1 option:
SELECT id FROM tbl ORDER BY Version DESC LIMIT 1

You mentioned you need this in a join, so something like this should do it
select *
from table_1 as t1
join (
select id,
row_number() over (order by version desc) as rn
from table_2
) as t2 on t1.id = t2.id and t2.rn = 1
(This is ANSI SQL as you didn't mention a DBMS - but should work on most modern DBMS)

Related

SQL Partition by with conditions

I want to partition the data on the basis of two columns Type and Env and fetch the top 5 records for each partition order by count desc. The problem that I'm facing is that I need to partition the Env on the basis of LIKE condition.
Data -
Type
Environment
Count
T1
E1
1
T1
M1
2
T1
AB1
3
T2
E1
1
T2
M1
2
T2
CB1
3
T2
M1
5
The result that I want - Let's say I'm fetching top (1) record for now
Type
Environment
Count
T1
M1
2
T1
AB1
3
T2
CB1
3
T2
M1
5
Here I'm dividing the env on condition (env LIKE "%M%" and env NOT LIKE "%M")
One approach that I can think of is using partition and union but this is a very expensive call due to the large amount of data that I'm filtering from. Is there a better way to achieve this?
SELECT
*
FROM
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY Type ORDER BY Count DESC) AS maxCount
FROM
table
WHERE
Env LIKE '%M%'
) AS t1
WHERE
t1.maxCount <= 5
UNION
SELECT
*
FROM
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY Type ORDER BY Count DESC) AS maxCount
FROM
table
WHERE
Env NOT LIKE '%M%'
) AS t1
WHERE
t1.maxCount <= 5
You would seem to want an additional partition by in your row_number():
select t.*
from (select t.*,
row_number() over (partition by type, case when environment like '%M%' then 1 else 2 end)
order by count desc
) as seqnum
from t
) t
where seqnum <= 5;

How to select the top 3 values from a group based on date and exclude duplicate value?

If I three columns and 1 column has ID, 1 column has value and 1 column has date. Example, ID column has ID1, ID2, ID3. The value for each ID has a numeric value, say 1,2,3,4,5 for each ID.
How do I only get 3 results for each ID based on the most recent date descending.
I am using Sybase SQL. Is there any way I can write this?
I tried to use Row_number() and rank() but I don't get to use either of those functions with my SQL tool.
ID value Date
1 3 20190511
1 1 20190503
1 5 20190401
2 2 20190520
2 1 20190514
2 4 20190503
3 1 20190516
3 5 20190415
3 3 20190402
If you don't have row_number try this
SELECT *
FROM yourTable t1
WHERE (SELECT COUNT(*)
FROM yourTable t2
WHERE t1.id = t2.id
AND t1.date < t2.date) < 3
So if one id have 3 or more older rows wont appear.
with row_number
SELECT *
FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY date DESC) as rn
FROM YourTable t1
) as t
WHERE t.rn <= 3
I assume you cant have multiple rows in same date. In that case you may want use RANK() or DENSE_RANK() and decide how handle ties.
One method uses a correlated subquery with in:
select t.*
from t
where t.date in (select top (3) t2.date
from t t2
where t2.id = t.id
order by t2.date desc
);
Note that this assumes that the dates are unique.

Fetching the next 3 or adjacent rows based upon a condition in postgreSQL

I have a database of more than 10,000 rows. eg:
id text
1 abc
2 ghj
3 cde
4 hif
5 klm
6 bbc
7 jkl
8 mno
9 dbo
10 ijk
I need to fetch the next three rows where the text matches a condition.
For eg: if I am doing a text like '%bc% query it should return me rows with ids 1,2,3,4,6,7,8,9 as row #1 and #6 is a match
Use below query to get the desired result. I am assuming you want to calculate next based on ID only and ID is always increment by 1, as you have mentioned in question.
If ID doesn't always increment by 1 , then first add a ROW Number and then replace id in t2 subquery and join condition with row number.
select t1.id, t1.id_text
from test t1
join
(
select id from test where id_text like '%bc%'
UNION
select id+1 from test where id_text like '%bc%'
UNION
select id+2 from test where id_text like '%bc%'
UNION
select id+3 from test where id_text like '%bc%'
) t2
on t1.id = t2.id;
SQL Fiddle Link
with -- Test data
t(i, x) as (values
(1,'abc'),(2,'ghj'),(3,'cde'),(4,'hif'),(5,'klm'),(6,'bbc'),(7,'jkl'),(8,'mno'),(9,'dbo'),(10,'ijk'))
select r.*
from
t as t0 cross join lateral (
select *
from t
where t.i >= t0.i
order by t.i
limit 4) as r
where t0.x like '%bc%'
order by r.i;
Lateral joins allows to use previous table in the next subquery.
You could use something like this:
SELECT next.*
FROM test, test next
WHERE test.text LIKE '%bc%'
AND (test.id + 1 = next.id OR test.id + 2 = next.id OR test.id + 3 = next.id)
I am not going to assume that the ids have no gaps. One method uses lag():
select t.*
from (select t.*,
lag(text) over (order by id) as prev_text,
lag(text, 2) over (order by id) as prev_text2,
lag(text, 3) over (order by id) as prev_text3
from t
) t
where text like '%bc%' or
prev_text like '%bc%' or
prev_text2 like '%bc%' or
prev_text3 like '%bc%';
You can also do this with one comparison, using other window functions:
select id, text
from (select t.*,
sum( (text like '%bc%')::int ) over (order by id rows between 3 preceding and current row) as cnt
from t
) t
where cnt > 0;
With an index on id, this might be the fastest approach to solving the problem.

Compare two rows in two columns

I have a table for example like below
column1 column2
110 100
50 125
120 80
I want a selection in such a way that i will get something like this
column1 column2 difference
110 100 0
50 125 50
120 80 5
or just to be able to identify the difference between first row of column2 and second row of column1
You can do this with a LEFT JOIN:
SQL Fiddle
WITH Cte AS(
SELECT *,
rn = ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM tbl
)
SELECT
t1.*,
difference = ISNULL(t2.column2 - t1.column1, 0)
FROM cte t1
LEFT JOIN Cte t2
ON t1.rn = t2.rn + 1
Since there is no column to indicate the order, I added a ROW_NUMBER. Modify the ORDER BY clause to your preference.
Another way, could be this:
SELECT TB.COLUMN1,TB.COLUMN2,
(ISNULL(TB2.COLUMN2,TB.COLUMN1)-TB.COLUMN1) AS 'DIF'
FROM
(SELECT COLUMN1,COLUMN2,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 AS 'R' FROM TEST ) TB
LEFT JOIN
(SELECT COLUMN1,COLUMN2,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS 'R' FROM TEST ) TB2
ON TB.R = TB2.R
Until before the post I didn't know how dont let row_number 'order by' affected the query, but based on the above answer, now I Know it, using select null ;) thank you #Felix Pamittan

SQL Query to Select the 'Next' record (similar to First or Top N)

I need to do a query to return the next (or prev) record if a certain record is not present. For instance consider the following table:
ID (primary key) value
1 John
3 Bob
9 Mike
10 Tom.
I'd like to query a record that has id 7 or greater if 7 is not present.
My questions are,
Are these type of queries possible with SQL?
What are such queries called in the DB world?
Thanks!
Yes, it's possible, but implementation will depend on your RDBMS.
Here's what it looks like in MySQL, PostgreSQL and SQLite:
select ID, value
from YourTable
where id >= 7
order by id
limit 1
In MS SQL-Server, Sybase and MS-Access:
select top 1 ID, value
from YourTable
where id >= 7
order by id
In Oracle:
select * from (
select ID, value
from YourTable
where id >= 7
order by id
)
where rownum = 1
In Firebird and Informix:
select first 1 ID, value
from YourTable
where id >= 7
order by id
In DB/2 (this syntax is in SQL-2008 standard):
select id, value
from YourTable
where id >= 7
order by id
fetch first 1 rows only
In those RDBMS that have "window" functions (in SQL-2003 standard):
select ID, Value
from (
select
ROW_NUMBER() OVER (ORDER BY id) as rownumber,
Id, Value
from YourTable
where id >= 7
) as tmp --- remove the "as" for Oracle
where rownumber = 1
And if you are not sure which RDBMS you have:
select ID, value
from YourTable
where id =
( select min(id)
from YourTable
where id >= 7
)
Try this for MS-SQL:
SELECT TOP 1
id, value
FROM your_table
WHERE id >= 7
ORDER BY id
or for MySql
SELECT id, value
FROM your_table
WHERE id >= 7
ORDER BY id
LIMIT 0,1
I would simply do it like this:
select top 1 * from myTable where id >=7
order by id
implementation of the top 1 part is T-SQL (MSSQL/Sybase), other implementations vary but it is always possible (mysql/postgre LIMIT 1, oracle rownum = 1)
select top 1 * from Persons where Id >= #Id order by Id