Selecting compared pairs from table - sql

I don't really know how to describe it. I have a table:
ID | Name | Date
-------------------------
1 | Mike | 01.01.2016
1 | Michael | 02.03.2016
2 | Samuel | 23.12.2015
2 | Sam | 05.03.2015
3 | Tony | 02.04.2012
I want to select pairs of IDs and Names with latest dates in each pair. The result here should be:
ID | Name | Date
-------------------------
1 | Michael | 02.03.2016
2 | Samuel | 23.12.2015
3 | Tony | 02.04.2012
How do I achieve this?
Oracle Database 11g

You can do it using the ROW_NUMBER() analytic function:
SELECT id, name, "date"
FROM (
SELECT t.*,
ROW_NUMBER() OVER ( PARTITION BY id ORDER BY "date" DESC ) rn
FROM table_name t
)
WHERE rn = 1
This requires only a single table scan (it does not have a self-join or correlated sub-query - i.e. IN (...) or EXISTS(...)).

Have a sub-select that returns each id and it's max date:
select * from table
where (id, date) in (select id, max(date) from table group by id)

You can use NOT EXISTS() :
SELECT * FROM YourTable t
WHERE NOT EXISTS(SELECT 1 FROM YourTable s
WHERE t.id = s.id and s.date > t.date)

Possibly the most efficient method is:
select t.*
from table t
where t.date = (select max(date) from table t2 where t2.id = t.id);
along with an index on table(id, date).
This version should scan the table and look up the correct value in the index.
Or, if there are only three columns, you can use keep:
select id, max(date) as date,
max(name) keep (dense_rank first order by date desc) as name
from table
group by id;
I have found that this version works very well in Oracle.

Related

How to select the last record of each ID

I need to extract the last records of each user from the table. The table schema is like below.
mytable
product | user_id |
-------------------
A | 15 |
B | 15 |
A | 16 |
C | 16 |
-------------------
The output I want to get is
product | user_id |
-------------------
B | 15 |
C | 16 |
Basically the last records of each user.
Thanks in advance!
You can use a window function called ROW_NUMBER.Here is a solution for you given below. I have also made a demo query in db-fiddle for you. Please check link Demo Code in DB-Fiddle
WITH CTE AS
(SELECT product, user_id,
ROW_NUMBER() OVER(PARTITION BY user_id order by product desc)
as RN
FROM Mytable)
SELECT product, user_id FROM CTE WHERE RN=1 ;
You can try using row_number()
select product,iserid
from
(
select product, userid,row_number() over(partition by userid order by product desc) as rn
from tablename
)A where rn=1
There is no such thing as a "last" record unless you have a column that specifies the ordering. SQL tables represent unordered sets (well technically, multisets).
If you have such a column, then use distinct on:
select distinct on (user_id) t.*
from t
order by user_id, <ordering col> desc;
Distinct on is a very handy Postgres extension that returns one row per "group". It is the first row based on the ordering specified in the order by clause.
You should have a column that stores the insertion order. Whether through auto increment or a value with date and time.
Ex:
autoIncrement
produt
user_id
1
A
15
2
B
15
3
A
16
4
C
16
SELECT produt, user_id FROM table inner join
( SELECT MAX(autoIncrement) as id FROM table group by user_id ) as table_Aux
ON table.autoIncrement = table_Aux.id

SQL select rows, where column value is unique (only appears once)

Given the table
| id | Name |
| 01 | Bob |
| 02 | Chad |
| 03 | Bob |
| 04 | Tim |
| 05 | Bob |
I want to select the name and ID, from rows where the name is unique (only appears once)
This is essentially the same as How to select unique values of a column from table?, but notice that the author doesn't need the id, so that problem can be solved by a GROUP BY name HAVING COUNT(name) = 1
However, I need to extract the entire row (could be tens or hundreds of columns) including the id, where COUNT(name) = 1, but I cannot GROUP BY id, name as every combination of those are unique.
EDIT:
Am using Google BigQuery.
Expected results:
| id | Name |
| 02 | Chad |
| 04 | Tim |
Simply do a GROUP BY. Use HAVING to make sure a name is only there once. Use MIN() to pick the only id for the name.
select min(id), name
from tablename
group by name
having count(*) = 1
Reading the table only once will increase performance! (And don't forget to create an index on (name, id).)
Use correlated subquery
DEMO
select * from tablename a
where not exists (select 1 from tablename b where a.name=b.name having count(*)>1)
OUTPUT:
id name
2 Chad
4 Tim
You can use NOT EXISTS :
SELECT t.*
FROM table t
WHERE NOT EXISTS (SELECT 1 FROM table t1 WHERE t1.name = t.Name AND t1.id <> t.id);
This would need index on table(id, name) to produce faster result set.
How about a simple aggregation?
select any_value(id), name
from t
group by name
having count(*) = 1;
BigQuery works quite well with aggregations so this might be quite efficient as well.
use exists and check uqique name
select id,name
from table t1
where exists ( select 1 from table t2 where t1.name=t2.name
having count(*)=1
)
Please try this.
SELECT
DISTINCT id,NAME
FROM
tableName
You can use multiple subqueries to extract what you need.
SELECT * FROM tableName
WHERE name IN (SELECT name FROM (SELECT name, COUNT(name) FROM tableName
GROUP BY name
HAVING COUNT(name) = 1) AS subQuery)
Below is for BigQuery Standard SQL and works for any number of columns w/o explicitly calling them out and does not require any join'ing or sub-selects
#standardSQL
SELECT t.*
FROM (
SELECT ANY_VALUE(t) t
FROM `project.dataset.table` t
GROUP BY name
HAVING COUNT(1) = 1
)

How to SELECT in SQL based on a value from the same table column?

I have the following table
| id | date | team |
|----|------------|------|
| 1 | 2019-01-05 | A |
| 2 | 2019-01-05 | A |
| 3 | 2019-01-01 | A |
| 4 | 2019-01-04 | B |
| 5 | 2019-01-01 | B |
How can I query the table to receive the most recent values for the teams?
For example, the result for the above table would be ids 1,2,4.
In this case, you can use window functions:
select t.*
from (select t.*, rank() over (partition by team order by date desc) as seqnum
from t
) t
where seqnum = 1;
In some databases a correlated subquery is faster with the right indexes (I haven't tested this with Postgres):
select t.*
from t
where t.date = (select max(t2.date) from t t2 where t2.team = t.team);
And if you wanted only one row per team, then the canonical answer is:
select distinct on (t.team) t.*
from t
order by t.team, t.date desc;
However, that doesn't work in this case because you want all rows from the most recent date.
If your dataset is large, consider the max analytic function in a subquery:
with cte as (
select
id, date, team,
max (date) over (partition by team) as max_date
from t
)
select id
from cte
where date = max_date
Notionally, max is O(n), so it should be pretty efficient. I don't pretend to know the actual implementation on PostgreSQL, but my guess is it's O(n).
One more possibility, generic:
select * from t join (select max(date) date,team from t
group by team) tt
using(date,team)
Window function is the best solution for you.
select id
from (
select team, id, rank() over (partition by team order by date desc) as row_num
from table
) t
where row_num = 1
That query will return this table:
| id |
|----|
| 1 |
| 2 |
| 4 |
If you to get it one row per team, you need to use array_agg function.
select team, array_agg(id) ids
from (
select team, id, rank() over (partition by team order by date desc) as row_num
from table
) t
where row_num = 1
group by team
That query will return this table:
| team | ids |
|------|--------|
| A | [1, 2] |
| B | [4] |

Getting distinct result with Oracle SQL

I have the following data structure
ID | REFID | NAME
1 | 100 | A
2 | 101 | B
3 | 101 | C
With
SELECT DISTINCT REFID, ID, NAME
FROM my_table
ORDER BY ID
I would like to have the following result:
1 | 100 | A
2 | 101 | B
Colum NAME and ID should contain the MIN or FIRST value.
But actually I get stuck at using MIN/FIRST here.
I welcome every tipps :-)
select id,
refid,
name
from (select id,
refid,
name,
row_number() over(partition by refid order by name) as rn
from my_table)
where rn = 1
order by id
You can use a subquery to do this.
WITH Q AS
( SELECT MIN(NAME) AS NAME, REFID FROM T GROUP BY REFID )
SELECT T.ID, T.REFID, T.NAME
FROM T
JOIN Q
ON (T.NAME = Q.NAME)
Also, note that SQL tables have no order. So there's no "First" value.

SQL Server : select from duplicate columns where date newest

I have inherited a SQL Server table in the (abbreviated) form of (includes sample data set):
| SID | name | Invite_Date |
|-----|-------|-------------|
| 101 | foo | 2013-01-06 |
| 102 | bar | 2013-04-04 |
| 101 | fubar | 2013-03-06 |
I need to select all SID's and the Invite_date, but if there is a duplicate SID, then just get the latest entry (by date).
So the results from the above would look like:
101 | fubar | 2013-03-06
102 | bar | 2013-04-04
Any ideas please.
N.B the Invite_date column has been declared as a nvarchar, so to get it in a date format I am using CONVERT(DATE, Invite_date)
You can use a ranking function like ROW_NUMBER or DENSE_RANK in a CTE:
WITH CTE AS
(
SELECT SID, name, Invite_Date,
rn = Row_Number() OVER (PARTITION By SID
Order By Invite_Date DESC)
FROM dbo.TableName
)
SELECT SID, name, Invite_Date
FROM CTE
WHERE RN = 1
Demo
Use Row_Number if you want exactly one row per group and Dense_Rank if you want all last Invite_Date rows for each group in case of repeating max-Invite_Dates.
select t1.*
from your_table t1
inner join
(
select sid, max(CONVERT(DATE, Invite_date)) mdate
from your_table
group by sid
) t2 on t1.sid = t2.sid and CONVERT(DATE, t1.Invite_date) = t2.mdate
select
SID,name,MAX(Invite_date)
FROM
Table1
GROUP BY
SID
http://sqlfiddle.com/#!2/6b6f66/1