SQL server - SELECT a columns entry on a CASE WHEN query - sql

I'd like to find out the first provider (PROVIDER_ID) to the client (CLIENT_ID) in a database table of bookings (BOOKING_ID)
I currently SELECT the CLIENT_ID first, then calculate various other things.
I group by (CLIENT_ID) and the count is correct.
What I'm looking for is
SELECT case when(min(BOOKING_ID)) then PROVIDER_ID else null end)
But I am unable to perform sub queries within the SELECT/CASE WHEN
I hope this makes sense and the question is clear.
Ideally I would like a solution that is within a single SELECT

Assuming you want to get the PROVIDER_ID for the MIN(BOOKING_ID) grouping by CLIENT_ID the following should work:
SELECT
Client_ID,
Booking_ID,
Provider_ID
FROM (
SELECT
Client_ID,
Provider_ID,
Booking_ID,
ROW_NUMBER() OVER (PARTITION BY Client_ID ORDER BY Booking_ID) as RowNumber
FROM
Bookings
) OrderedTable
WHERE
OrderedTable.RowNumber = 1
How does it work? ROW_NUMBER OVER (ORDER BY field) gives you the row number if the result set was ordered by a particular field. The PARTITION BY field allows you to partition the table by a particular key (in this case Client_ID) that will reset the ROW_NUMBER for each Client_ID (so if RowNumber = 1, it's the first entry for that particular client)
More details here: http://msdn.microsoft.com/en-us/library/ms186734.aspx
Using WITH syntax:
WITH OrderedTable AS
(
SELECT
Client_ID,
Provider_ID,
Booking_ID,
ROW_NUMBER() OVER (PARTITION BY Client_ID ORDER BY Booking_ID) as RowNumber
FROM
Bookings
)
SELECT
Client_ID,
Provider_ID,
Booking_ID
FROM
OrderedTable
WHERE
RowNumber = 1

Related

PostgresSQL select

Im beginner in sql, so i need help...
I have tabe like this:
enter image description here
I need that my query bring back service_id: 5 and id:8, 2 rows(or its depends how many client_ids ill put in my where condition), with client_id, service_type_id, service_id, because service_start_date is max on those 2 clients. I have a full list of clients and i need to do it, thanks for the answers.
i used row_number() OVER( PARTITION BY client_id ORDER BY (service_start_date) desc)
it will give rownumber with client_id service_start_date descending
select
*
from (select
service_id,
service_name,
service_start_date,
service_end_date,
client_id,
service_type_id,
row_number() OVER( PARTITION BY client_id ORDER BY (service_start_date) desc) as ord
from servicetable
/* add where condition here*/
order by service_id) as drt
where ord=1

How to grab the last value in a column per user for the last date

I have a table that contains three columns: ACCOUNT_ID, STATUS, CREATE_DATE.
I want to grab only the LAST status for each account_id based on the latest create_date.
In the example above, I should only see three records and the last STATUS per that account_2.
Do you know a way to do this?
create table TBL 1 (
account_id int,
status string,
create_date date)
select account_id, max(create_date) from table group by account_id;
will give you the account_id and create_date at the closest past date to today (assuming create_date can never be in the future, which makes sense).
Now you can join with that data to get what you want, something along the lines for example:
select account_id, status, create_date from table where (account_id, create_date) in (<the select expression from above>);
If you use that frequently (account with the latest create date), then consider defining a view for that.
If you have many columns and want keep the row that is the last line, you can use QUALIFY to run the ranking logic, and keep the best, like so:
SELECT *
FROM tbl
QUALIFY row_number() over (partition by account_id order by create_date desc) = 1;
The long form is the same pattern the Ely shows in the second answer. But with the MAX(CREATE_DATE) solution, if you have two rows on the same last day, the IN solution with give you both. you can also get via QUALIFY if you use RANK
So the SQL is the same as:
SELECT account_id, status, create_date
FROM (
SELECT *,
row_number() over (partition by account_id order by create_date desc) as rn
FROM tbl
)
WHERE rn = 1;
So the RANK for, which will show all equal rows is:
SELECT *
FROM tbl
QUALIFY rank() over (partition by account_id order by create_date desc) = 1;

Select MAX Value for Each ROW - Oracle Sql

I have one doubt.
I need to find what is the latest occurrence for a specific list of Customers, let's say to simplify, I need it for 3 Customers out of 100.
I need to check when it was the last time each of them got a bonus.
The table would be:
EVENT_TBL
Fields: Account ID, EVENT_DATE, BONUS ID, ....
Can you suggest a way to grab the latest (MAX) EVENT DATE (that means one row each)
I'm using SELECT...IN to specify the Account ID but not sure how to use MAX, Group BY etc etc (if ever needed).
Use the ROW_NUMBER() analytic function:
SELECT *
FROM (
SELECT t.*,
ROW_NUMBER() OVER ( PARTITION BY Account_id ORDER BY event_date DESC ) AS rn
FROM EVENT_TBL t
WHERE Account_ID IN ( 123, 456, 789 )
)
WHERE rn = 1
You can try
with AccountID_Max_EVENT_DATE as (
select AccountID, max(EVENT_DATE) MAX_D
from EVENT_TBL
group by AccountID
)
SELECT E.*
FROM EVENT_TBL E
INNER JOIN AccountID_Max_EVENT_DATE M
ON (E.AccountID = M.AccountID AND M.MAX_D = E.EVENT_DATE)

Query to select 2 most recent dated records per grouping

Using R, I want to grab the two most recently dated entries for each UserID, assuming there is 1 or more entries per UserID.
The key elements of my data would be an identifier (UserID), and a date, that is of type date.
Thank you.
In SQL Server, which has the ROW_NUMBER() analytic function, you can try this query:
SELECT t.UserID, t.date, ...other columns
FROM
(
SELECT UserID, date, ...other columns,
ROW_NUMBER() OVER (PARTITION BY UserID ORDER BY date DESC) rn
FROM yourTable
) t
WHERE t.rn <= 2

How to find duplicate records in PostgreSQL

I have a PostgreSQL database table called "user_links" which currently allows the following duplicate fields:
year, user_id, sid, cid
The unique constraint is currently the first field called "id", however I am now looking to add a constraint to make sure the year, user_id, sid and cid are all unique but I cannot apply the constraint because duplicate values already exist which violate this constraint.
Is there a way to find all duplicates?
The basic idea will be using a nested query with count aggregation:
select * from yourTable ou
where (select count(*) from yourTable inr
where inr.sid = ou.sid) > 1
You can adjust the where clause in the inner query to narrow the search.
There is another good solution for that mentioned in the comments, (but not everyone reads them):
select Column1, Column2, count(*)
from yourTable
group by Column1, Column2
HAVING count(*) > 1
Or shorter:
SELECT (yourTable.*)::text, count(*)
FROM yourTable
GROUP BY yourTable.*
HAVING count(*) > 1
From "Find duplicate rows with PostgreSQL" here's smart solution:
select * from (
SELECT id,
ROW_NUMBER() OVER(PARTITION BY column1, column2 ORDER BY id asc) AS Row
FROM tbl
) dups
where
dups.Row > 1
In order to make it easier I assume that you wish to apply a unique constraint only for column year and the primary key is a column named id.
In order to find duplicate values you should run,
SELECT year, COUNT(id)
FROM YOUR_TABLE
GROUP BY year
HAVING COUNT(id) > 1
ORDER BY COUNT(id);
Using the sql statement above you get a table which contains all the duplicate years in your table. In order to delete all the duplicates except of the the latest duplicate entry you should use the above sql statement.
DELETE
FROM YOUR_TABLE A USING YOUR_TABLE_AGAIN B
WHERE A.year=B.year AND A.id<B.id;
You can join to the same table on the fields that would be duplicated and then anti-join on the id field. Select the id field from the first table alias (tn1) and then use the array_agg function on the id field of the second table alias. Finally, for the array_agg function to work properly, you will group the results by the tn1.id field. This will produce a result set that contains the the id of a record and an array of all the id's that fit the join conditions.
select tn1.id,
array_agg(tn2.id) as duplicate_entries,
from table_name tn1 join table_name tn2 on
tn1.year = tn2.year
and tn1.sid = tn2.sid
and tn1.user_id = tn2.user_id
and tn1.cid = tn2.cid
and tn1.id <> tn2.id
group by tn1.id;
Obviously, id's that will be in the duplicate_entries array for one id, will also have their own entries in the result set. You will have to use this result set to decide which id you want to become the source of 'truth.' The one record that shouldn't get deleted. Maybe you could do something like this:
with dupe_set as (
select tn1.id,
array_agg(tn2.id) as duplicate_entries,
from table_name tn1 join table_name tn2 on
tn1.year = tn2.year
and tn1.sid = tn2.sid
and tn1.user_id = tn2.user_id
and tn1.cid = tn2.cid
and tn1.id <> tn2.id
group by tn1.id
order by tn1.id asc)
select ds.id from dupe_set ds where not exists
(select de from unnest(ds.duplicate_entries) as de where de < ds.id)
Selects the lowest number ID's that have duplicates (assuming the ID is increasing int PK). These would be the ID's that you would keep around.
Inspired by Sandro Wiggers, I did something similiar to
WITH ordered AS (
SELECT id,year, user_id, sid, cid,
rank() OVER (PARTITION BY year, user_id, sid, cid ORDER BY id) AS rnk
FROM user_links
),
to_delete AS (
SELECT id
FROM ordered
WHERE rnk > 1
)
DELETE
FROM user_links
USING to_delete
WHERE user_link.id = to_delete.id;
If you want to test it, change it slightly:
WITH ordered AS (
SELECT id,year, user_id, sid, cid,
rank() OVER (PARTITION BY year, user_id, sid, cid ORDER BY id) AS rnk
FROM user_links
),
to_delete AS (
SELECT id,year,user_id,sid, cid
FROM ordered
WHERE rnk > 1
)
SELECT * FROM to_delete;
This will give an overview of what is going to be deleted (there is no problem to keep year,user_id,sid,cid in the to_delete query when running the deletion, but then they are not needed)
In your case, because of the constraint you need to delete the duplicated records.
Find the duplicated rows
Organize them by created_at date - in this case I'm keeping the oldest
Delete the records with USING to filter the right rows
WITH duplicated AS (
SELECT id,
count(*)
FROM products
GROUP BY id
HAVING count(*) > 1),
ordered AS (
SELECT p.id,
created_at,
rank() OVER (partition BY p.id ORDER BY p.created_at) AS rnk
FROM products o
JOIN duplicated d ON d.id = p.id ),
products_to_delete AS (
SELECT id,
created_at
FROM ordered
WHERE rnk = 2
)
DELETE
FROM products
USING products_to_delete
WHERE products.id = products_to_delete.id
AND products.created_at = products_to_delete.created_at;
Following SQL syntax provides better performance while checking for duplicate rows.
SELECT id, count(id)
FROM table1
GROUP BY id
HAVING count(id) > 1
begin;
create table user_links(id serial,year bigint, user_id bigint, sid bigint, cid bigint);
insert into user_links(year, user_id, sid, cid) values (null,null,null,null),
(null,null,null,null), (null,null,null,null),
(1,2,3,4), (1,2,3,4),
(1,2,3,4),(1,1,3,8),
(1,1,3,9),
(1,null,null,null),(1,null,null,null);
commit;
set operation with distinct and except.
(select id, year, user_id, sid, cid from user_links order by 1)
except
select distinct on (year, user_id, sid, cid) id, year, user_id, sid, cid
from user_links order by 1;
except all also works. Since id serial make all rows unique.
(select id, year, user_id, sid, cid from user_links order by 1)
except all
select distinct on (year, user_id, sid, cid)
id, year, user_id, sid, cid from user_links order by 1;
So far works nulls and non-nulls.
delete:
with a as(
(select id, year, user_id, sid, cid from user_links order by 1)
except all
select distinct on (year, user_id, sid, cid)
id, year, user_id, sid, cid from user_links order by 1)
delete from user_links using a where user_links.id = a.id returning *;