Get the smallest start date for several elements and group them? - sql

I have this table called "Class". Class has Class ID, Room ID, and Class Start Time.
For example:
Class_ID: 1
Room_ID: 1234
Class_Start_Time: 07/11/2016 1 pm
Class_ID: 2
Room_ID: 1234
Class_Start_Time: 07/11/2016 9 am
Class_ID: 4
Room_ID: 1235
Class_Start_Time: 07/11/2016 8 am
I need to get the smallest start time for each room. Meaning that Room 1234 would get 07/11/2016 9 am and Room 1235 would get 07/11/2016 8 am
(only one start time). I tried this:
SELECT Room_ID
FROM Courses
GROUP BY Room_ID
ORDER BY Class_Start_Time
It asks me to include the start time in the group by, but if I do so, it would give me all the start times and repeated rooms, when what I need is only one of each room and ordered by the room's respective start time. So...
Room_ID = 1235
Start time = 07/11/2016 8 am
Room_ID = 1234
Start time = 07/11/2016 9 am
The order of the rooms doesn't matter is the order of each smallest start time that does. Any suggestions?

You can use the ranking functions in SQL Server such as the Row_Number.
You can do some reading here:
https://msdn.microsoft.com/en-us/library/ms186734.aspx
I think this is what you are looking for.
Create Table Courses
(
ClassId int null,
Room_Id int null,
Class_Start_Time datetime null
)
Insert into Courses
values ('1','1234','2016-07-11 13:00:00.000')
Insert into Courses
values ('2','1234','2016-07-11 09:00:00.000')
Insert into Courses
values ('4','1235','2016-07-11 08:00:00.000')
Select t.ClassID,t.Room_Id,t.Class_Start_Time from
(
SELECT *,row_number() over (partition by Room_Id order by Class_Start_Time) as SequenceNumber
from Courses
)t
Where t.SequenceNumber=1
order by Class_Start_Time

Use MIN(), give your Class_Start_Time an alias and order by it.
SELECT Room_Id, MIN(Class_Start_Time) Start
FROM Courses
GROUP BY Room_Id
ORDER BY Start

Related

Turn several queries into one in SQL Server

I have a table in SQL Server called schedule that has the following columns (and others not listed):
scheduleId
roomId
dateRegistered
dateFreed
4564
2
2022-12-25
2022-12-26
4565
3
2022-12-25
2022-12-27
4566
15
2022-12-26
2022-12-27
4567
2
2022-12-28
2022-12-31
4568
3
2022-12-28
2022-12-30
In some part of my app I need to show all the rooms occupied at a certain date.
Currently I run a query like this:
SELECT TOP (1) *
FROM schedule
WHERE roomId = [theNeededRoom] AND dateFreed < [providedDate]
ORDER BY dateFreed DESC
The thing is that I have to run that query in a for loop so that I get the information for every room.
I'm sure there has to be a better way to do this in a single query that returns a row for each of the different roomIds possible, how can I go about this?
Also, when the room is first registered, the dateFreed column has a null value, if I wanted to take this into account, how can I make the query so that, in the case the dateFreed value is null, that is the row that gets chosen?
You can use TOP(1) WITH TIES, while ordering on the last "dateFreed" date.
In order to have a "tied" value to match on, instead of ordering on "dateFreed DESC" we can use the ROW_NUMBER window function to generate a ranking on the same field (which will store 1 for each most recent "dateFreed" value, per "roomId").
SELECT TOP (1) WITH TIES *
FROM schedule
WHERE dateFreed < [providedDate]
ORDER BY ROW_NUMBER() OVER(PARTITION BY roomId ORDER BY dateFreed DESC)
SELECT
t.*
FROM
(
SELECT
roomId AS rId,
max(dateFreed) AS dateFreedMax
FROM
schedule s
GROUP BY
s.roomId
) AS t
WHERE
t.dateFreedMax < [providedDate]
OR t.dateFreedMax IS NULL
Or
SELECT roomId
FROM
schedule s
GROUP BY s.roomId, dateFreed
HAVING
max(dateFreed)<[providedDate] OR dateFreed IS NULL

Query to get only cars inside car park

I want to do a report from a car park, and with this car park get the cars that are inside at the moment when i call the query.
Supose that we have two entrances and two exits, how i can do in SQL to get only cars inside that parking.
How I can get this values?
Example of records of my table:
id lic plate date lane access id_user
__________________________________________________________________
10 1234-BK 2020-08-11 12:24:00.000 1 OK 4
11 1234-BK 2020-08-11 12:25:00.000 3 OK 4
With that example we supose that this car is out of the car park because lane 1 is from entrances and lane 3 is from exit, so the last record we have is from a exit lane.
With this information could you orientate me to do this query that get all the cars inside?
You should be storing whether cars are entering or leaving. That seems pretty basic for such an application.
If you don't have that, you can count the number of records up to a given time and if the value is odd the car is in and even it is out. So to get the cars inside:
select lic_plate
from t
where date < #date
group by lic_plate
having count(*) % 2 = 1;
If you have entrance and exit lanes, you can get the last record using a correlated subquery and check for the final lance:
select t.*
from t
where t.date = (select max(t2.date)
from t t2
where t2.lic_plate = t.lic_plate and
t2.date < #date
) and
t.lane = 1; -- last lane is an entrance lane
To get rows where the last 'lane' for each 'lic_plate' is lane 1 the OP could use a windowing function.
;with get_max_cte as (
select t.*, ROW_NUMBER() over (partition by lic_plate order by t.[date] desc) rn)
select * from get_max_cte
where rn=1 and lane=1

Finding a min() date for one column and then using this to join with other tables that have a date LESS than this date

In short, I have two tables:
(1) pharmacy_claims (columns: user_id, date_service, claim_id, record_id, prescription)
(2) medical_claims (columns: user_id, date_service, provider, npi, cost)
I want to find user_id's in (1) that have a certain prescription value, find their earliest date_service (e.g. min(date_service)) and then use these user_id's with their earliest date of service as a cohort to pull all of their associated data from (2). Basically I want to find all of their medical_claims data PRIOR to the first time they were prescribed a given prescription in pharmacy_claims.
pharmacy_claims looks something like this:
user_id | prescription | date_service
1 a 2018-05-01
1 a 2018-02-11
1 a 2019-10-11
1 b 2018-07-12
2 a 2019-01-02
2 a 2019-03-10
2 c 2018-04-11
3 c 2019-05-26
So for instance, if I was interested in prescription = 'a', I would only want user_id 1 and 2 returned, with dates 2018-02-11 and 2019-01-02, respectively. Then I would want to pull user_id 1 and 2 from the medical_claims, and get all of their data PRIOR to these respective dates.
The way I tried to go about this was to build out a temp table in the pharmacy_claims table to query the user_id's that have a given medication, and then left join this back to the table to create a cohort of user_id's with a date_service
Here's what I did:
(1) Pulled all of the relevant data from the main pharmacy claims table:
CREATE TABLE user.temp_pharmacy_claims AS
SELECT user_id, claim_id, record_id, date_service
FROM dw.pharmacyclaims
WHERE date_service between '2018-01-01' and '2019-08-31'
This results in ~50,000 user_id's
(2) Created a table with just the user_id's a min(date_service):
CREATE TABLE user.temp_pharmacy_claims_index AS
SELECT distinct user_id, min(date_service) AS Min_Date
FROM user.temp_pharmacy_claims
GROUP BY 1
(3) Created a final table (to get the desired cohort):
CREATE TABLE user.temp_pharmacy_claims_final_index AS
SELECT a.userid
FROM user.temp_pharmacy_claims a
LEFT JOIN user.temp_pharmacy_claims_index b
ON a.user = b.user
WHERE a.date_service < Min_Date
However, this gets me 0 results when there should be a few thousand. Is this set up correctly? It's probably not the most efficient approach, but it looks sound to me, so not sure what's going on.
I think you just want a correlated subquery:
select mc.*
from medical_claims mc
where mc.date_service < (select min(pc.date)
from pharmacy_claims pc
where pc.user_id = mc.user_id and
pc.prescription = ?
);

Select duplicate by youngest date

I have run into a snag.
A got a DB with employees with multiple startdates.
Employees can start and can get a new contract later.
FE.
ID NAME DATEEMPLOYED FUNCTION
1 Paul 01/01/2016 Director
2 Paul 01/01/2015 Staff Member
3 Jeff 02/05/2016 Director
4 Jeff 01/05/2015 Employee
5 Jeff 01/05/2014 Employee
6 Eric 05/06/2015 Employee
Now I need to get the ID from the latest and the youngest date.
I want to copy the function of the row with the latest date to the oldest date and then delete all but the oldest.
The oldest I can find by:
SELECT * FROM [database].[dbo].[Personel] t WHERE DATEEMPLOYED NOT IN (SELECT MAX(DATEEMPLOYED) AS LastUpdate FROM [database].[dbo].[Personel] GROUP BY Naam,Voornaam)
This returns 10 rows...
Now to find the youngest...
I thought, it would be as easy as changing MAX(DATEEMPLOYED) to MIN(DATEEMPLOYED)...
But I guess not because this only returns 6 rows...
I'm running a live DB so no sample date...
The expected output of the query for the max date per employee is ID 1 and 3 ... The expected output for min date is ID 2 and 5 ...
No number 6
I'am running MS SQL trough an ASP.net application...
The query posted I'm running on the SQL server itself for testing...
Later I'll adapt for the ASP.Net
I want to automatize the deletion of duplicate employees.
Where did i go wrong?
DEPENDING on your version, you could use the window function
Declare #YourTable table (ID int,NAME varchar(50), DATEEMPLOYED Date, [FUNCTION] varchar(50))
Insert Into #YourTable values
(1,'Paul','01/01/2016','Director'),
(2,'Paul','01/01/2015','Staff Member'),
(3,'Jeff','02/05/2016','Director'),
(4,'Jeff','01/05/2015','Employee'),
(5,'Jeff','01/05/2014','Employee'),
(6,'Eric','05/06/2015','Employee')
;with cteBase as (
Select Distinct Name
,Times = count(*) over (Partition By Name)
,MinID = min(ID) over (Partition By Name)
,MaxID = max(ID) over (Partition By Name)
,MinDate = min(DATEEMPLOYED) over (Partition By Name Order By DATEEMPLOYED)
,MaxDate = max(DATEEMPLOYED) over (Partition By Name Order By DATEEMPLOYED Desc)
From #YourTable
)
Select * from cteBase where Times>1
Returns
Name Times MinID MaxID MinDate MaxDate
Jeff 3 3 5 2014-01-05 2016-02-05
Paul 2 1 2 2015-01-01 2016-01-01
Does your DB have NULL dates, if so you need to check for those.

How to increment a value in SQL based on a unique key

Apologies in advance if some of the trigger solutions already cover this but I can't get them to work for my scenario.
I have a table of over 50,000 rows, all of which have an ID, with roughly 5000 distinct ID values. There could be 100 rows with an instrumentID = 1 and 50 with an instrumentID = 2 within the table etc but they will have slightly different column entries. So I could write a
SELECT * from tbl WHERE instrumentID = 1
and have it return 100 rows (I know this is easy stuff but just to be clear)
What I need to do is form an incrementing value for each time a instrument ID is found, so I've tried stuff like this:
IntIndex INT IDENTITY(1,1),
dDateStart DATE,
IntInstrumentID INT,
IntIndex1 AS IntInstrumentID + IntIndex,
at the table create step.
However, I need the IntIndex1 to increment when an instrumentID is found, irrespective of where the record is found in the table so that it effectively would provide a count of the records just by looking at the last IntIndex1 value alone. Rather than what the above does which is increment on all of the rows of the table irrespective of the instrumentID so you would get 5001,4002,4003 etc.
An example would be: for intInstruments 5000 and 4000
intInstrumentID | IntIndex1
--------- ------------------
5000 | 5001
5000 | 5002
4000 | 4001
5000 | 5003
4000 | 4002
The reason I need to do this is because I need to join two tables based on these values (a start and end date for each instrumentID). I have tried GROUP BY etc but this can't work in both tables and the JOIN then doesn't work.
Many thanks
I'm not entirely sure I understand your problem, but if you just need IntIndex1 to join to, could you just join to the following query, rather than trying to actually keep the calculated value in the database:
SELECT *,
intInstrumentID + RANK() OVER(PARTITION BY intInstrumentID ORDER BY dDateStart ASC) AS IntIndex1
FROM tbl
Edit: If I understand your comment correctly (which is not certain!), then presumably, you know that your end date and start date tables have the exact same number of rows, which leads to a one to one mapping between them based on thir respective end dates within instrument id?
If that's the case then maybe this join is what you are looking for:
SELECT SD.intInstrumentID, SD.dDateStart, ED.dEndDate
FROM
(
SELECT intInstrumentID,
dStartDate,
RANK() OVER(PARTITION BY intInstrumentID ORDER BY dDateStart ASC) AS IntIndex1
FROM tblStartDate
) SD
JOIN
(
SELECT intInstrumentID,
dEndDate,
RANK() OVER(PARTITION BY intInstrumentID ORDER BY dEndDate ASC) AS IntIndex1
FROM tblStartDate
) ED
ON SD.intInstrumentID = ED.intInstrumentID
AND SD.IntIndex1 = ED.IntIndex1
If not, please will you post some example data for both tables and the expected results?