SQL Query for getting data from different columns of different rows - sql

I have a dataset as following:
VN FNAME SEG STARTTIME ENDTIME F1 DIS F2
5 try 1 09-DEC-21 10.00.00 PM 09-DEC-21 11.05.00 PM 0 2 1
1 eat 1 09-DEC-21 11.00.00 PM 09-DEC-21 11.59.59 PM 1 15 1
5 sit 1 09-DEC-21 11.30.00 PM 09-DEC-21 11.59.59 PM 0 21 1
1 eat 2 10-DEC-21 12.00.00 AM 10-DEC-21 02.00.00 AM 1 15 1
5 sit 2 10-DEC-21 12.00.00 AM 10-DEC-21 04.00.00 AM 0 21 1
9 fly 1 10-DEC-21 01.00.00 AM 10-DEC-21 04.30.00 AM 1 50 1
4 say 1 10-DEC-21 05.00.00 AM 10-DEC-21 06.30.00 AM 0 25 1
With the above dataset I want to fetch records with unique FNAME where F1 and F2 both are 1 but STARTTIME displays the STARTTIME where SEG = 1 and ENDTIME displays the ENDTIME where SEG = max(SEG) for the FNAME. So basically the result which I am looking at is:
VN FNAME STARTTIME ENDTIME DIS
1 eat 09-DEC-21 11.00.00 PM 10-DEC-21 02.00.00 AM 15
9 fly 10-DEC-21 01.00.00 AM 10-DEC-21 04.30.00 AM 50
How can I achieve this using a SQL query? The database I am working with is Oracle.
Any help appreciated. Many thanks in advance.

Something like so, using grouing and subqueries. Having count(*) as 1 will show where there is 1 instance of the name, with F1 & F2 being 1.
select t.fname,
(select t2.starttime from table1 as t2 where t2.fname=t.fname and t2.seg=1) as start_time,
max(t.endtime) as end_time
from table1 as t
where t.f1=1 and t.f2=1
group by t.fname
having count(*)=1

Related

SQL Self join with closest time stamp

I have a table on a SQL Server with millions of lab results from different people (AA,BB etc.). Some substances, for instance sodium and potassium, may be measured by two different methods (A and B).
I would now like to do some sort of "self join" based on the following criteria:
I would like to compare the results of the same substance obtained 1) from the same person, 2) with the two different methods and 3) with the closest date/time stamp
For instance, each SampleID of Sodium_A analysis should be joined with the Sodium_B analysis from the same person with the closest date/time stamp:
Source table:
select T1.DateTime, T1.PersonID, T1.SampleID, A1.Analysis, T1.Result
from [DataTable] T1 LEFT JOIN
[AnalysisTable] A1
ON A1.DW_SK_Analyse = T1.DW_SK_Analyse
DateTime
PersonID
SampleID
Analysis
Result
01-01-2021 10:30
AA
1
Sodium_A
10
01-01-2021 10:30
AA
1
Potassium_A
5
10-01-2021 11:30
AA
2
Sodium_A
15
10-01-2021 11:30
AA
2
Potassium_A
15
11-02-2021 12:30
AA
3
Sodium_A
20
16-03-2021 13:30
AA
4
Sodium_A
9
18-04-2021 14:30
AA
5
Sodium_A
1
02-01-2021 10:30
AA
6
Sodium_B
9
20-03-2021 13:30
AA
9
Sodium_B
11
20-04-2021 14:30
AA
10
Sodium_B
2
20-04-2021 14:30
AA
10
Potassium_B
6
23-05-2021 12:50
BB
13
Sodium_B
58
26-05-2021 11:20
BB
14
Potassium_A
11
29-05-2021 12:20
BB
15
Sodium_A
15
30-06-2021 11:20
BB
16
Sodium_B
24
30-06-2021 11:20
BB
16
Potassium_B
21
Desired result:
DateTime1
DateTime2
PersonID
SampleID1
SampleID2
Analysis1
Analysis2
Result1
Result2
01-01-2021 10:30
02-01-2021 10:30
A
1
6
Sodium_A
Sodium_B
10
9
10-01-2021 10:30
02-01-2021 10:30
A
2
6
Sodium_A
Sodium_B
15
9
29-05-2021 12:20
23-05-2021 12:50
B
15
13
Sodium_A
Sodium_B
15
58
I hope it makes sense... :-) Any idea on how to do this?
I've started with something like this, but it does not consider the time difference:
select
T1.DateTime, T1.PersonID, T1.SampleID, A1.Analysis, T1.Result,
T2.DateTime, T2.PersonID, T2.SampleID, A2.Analysis, T2.Result
from [DataTable] T1
LEFT JOIN [AnalysisTable] A1 ON A1.DW_SK_Analyse=T1.DW_SK_Analyse
INNER JOIN [DataTable] T2 ON T1.PersonID=T2.PersonID
LEFT JOIN [AnalysisTable] A2 ON A2.DW_SK_Analyse=T2.DW_SK_Analyse
WHERE (select AnaType1 =
CASE
WHEN A1.Analysis='Sodium_A' THEN 'Sodium'
WHEN A1.Analysis='Potassium_A' THEN 'Potassium'
else 'NN' end) = (select AnaType2 =
CASE
WHEN A2.Analysis='Sodium_B' THEN 'Sodium'
WHEN A2.Analysis='Potassium_B' THEN 'Potassium'
else 'OO'
end )
You can use apply:
with t as (
select T1.DateTime, T1.PersonID, T1.SampleID, A1.Analysis, T1.Result,
left(A1.Analysis, len(A1.Analysis) - 2) as substance,
right(A1.Analysis, 1) as which
from [DataTable] T1 left join
[AnalysisTable] A1
on A1.DW_SK_Analyse = T1.DW_SK_Analyse
)
select t.*, t_other.*
from t outer apply
(select top (1) t_other.*
from t t_other
where t_other.personId = t.personId and
t_other.substance = t.substance and
t_other.which <> t.which
order by abs(datediff(second, t.datetime, t_other.datetime))
) t_other;
Note that I split the "analysis" into two parts, one for the substance and one for the particular test.

SELECTING records based on unique date and counting how many records on that date

I have a table that I'm going to simplify. Here's what it looks like:
tid session pos dateOn
-----------------------------------------------
1 23 0 12/24/2020 1:00:00
2 23 1 12/24/2020 1:01:23
3 12 0 12/24/2020 1:02:43
4 23 2 12/24/2020 1:04:01
5 23 3 12/24/2020 1:04:12
6 45 0 12/26/2020 4:23:15
This table tells me that there were 2 unique sessions 12/24/2020 and 1 on 12/26.
How do I write my SQL statement so that I get a result like this:
date recordCount
----------------------------
12/24/2020 2
12/26/2020 1
You should simply be able to convert to a date and aggregate:
select convert(date, dateon), count(distinct session)
from t
group by convert(date, dateon)
order by convert(date, dateon);

return the count of row even if null sql server

I trying to do a sql query to get the count for shift for each user
I used this query :
SELECT
COUNT(s.id) AS count, s.user_id
FROM
sarcuser AS u
INNER JOIN
sarcshiftpointuser AS s ON s.user_id = u.id
INNER JOIN
sarcalllevel AS l ON l.id = u.levelid
INNER JOIN
sarcshiftpointtable AS t ON t.shift_id = s.shift_id AND s.table_id = t.table_id
WHERE
(s.shift_id + '' LIKE '2')
AND (CAST(s.xdate AS DATE) BETWEEN CAST(N'2014-01-01' AS DATE) AND CAST(N'2015-01-01' AS DATE))
AND (u.gender + '' LIKE N'%')
AND (u.levelid + '' LIKE N'%')
AND (s.point_id + '' LIKE '2')
GROUP BY
s.user_id
ORDER BY
count
It works very well ... but there is a logic problem :
when the user didn't appear in the shift didn't return the count and I need it to return 0
For example :
user1 user2
shift1 2 2
shift2 5 0
shift3 6 10
but actually the code returns :
user1 user2
shift1 2 2
shift2 5 10
shift3 6
and that's wrong ... how to return the count even if it zero with this condition and this inner join ?
Sample for data in table :
sarcuser :
id firstname lastname gender levelid
52 samy sammour male 1
62 ibrahim jackob male 1
71 rebeca janson female 3
sarcalllevel :
id name
1 field leader
2 leader
3 paramdic
sarcshiftpointtable :
id shift_id table_id name_of_shift point_id
1 1 1 shift1 2
2 2 1 shift2 2
3 3 1 shift3 2
4 1 2 shift1 7
5 2 2 shift2 7
6 3 2 shift3 7
sarcshiftpointuser :
id point_id shift_id table_id user_id xdate
1 2 1 1 62 2014-01-05
2 2 1 1 0 2014-01-05
3 2 1 1 71 2014-01-05
4 2 2 1 0 2014-01-05
5 2 2 1 0 2014-01-05
6 2 2 1 52 2014-01-05
7 2 3 1 52 2014-01-05
8 2 3 1 62 2014-01-05
9 2 3 1 71 2014-01-05
10 2 1 1 71 2014-01-06
11 2 1 1 52 2014-01-06
12 2 1 1 0 2014-01-06
13 2 2 1 62 2014-01-06
14 2 2 1 0 2014-01-06
15 2 2 1 52 2014-01-06
16 2 3 1 62 2014-01-06
17 2 3 1 52 2014-01-06
18 2 3 1 71 2014-01-06
if i apply this query 3 times by changing the shift should return :
52 62 71
shift1 1 2 2
shift2 2 1 0
shift3 2 2 2
in shift2 in sarcshiftpointuser the user 71 is not appear
so when I do the code it will return just to field not three ? the count 0 is not returned
52 62 71
shift2 2 1
to be more specific :
I need to export this table into excel so when the 0 is not return it give me a wrong order and wrong value (logically )
You will need to use a nested query using IFNULL
Take a look to this
http://www.w3schools.com/sql/sql_isnull.asp
Something like,
IFNULL(user,0)
I think you are referring a crosstab query. you can use PIVOT to return your result set. Please refer below link.
Sql Server 2008 Cross Tab Query.
If you give few sample data for sarcuser , sarcshiftpointuser, sarcalllevel & sarcshiftpointtable tables, then we can give you a better answer.

T-SQL recursion

I have a set of data that looks like below
Name Time Perc Group Mode Control Cancelled
A 10:52 10.10 10 0 1 0
B 09:00 10.23 10 1 1 1
C 12:02 12.01 12 0 1 1
D 10:45 12.12 12 1 7 1
E 12:54 12.56 12 1 3 0
F 01:01 13.90 13 0 11 1
G 02:45 13.23 13 1 12 1
H 09:10 13.21 13 1 1 0
I need an output like below;
Group Perc Cancelled
10 20.33 1
12 36.69 2
13 40.34 2
What I'm getting was something like;
Group Perc Cancelled
10 20.33 5
12 36.69 5
13 40.34 5
I don't know what to call this, I have something in my mind to call it like CTE?, but I really can't figure it out.
Here's my source;
SELECT Group, SUM(Perc), Cancelled FROM
(SELECT Group, Perc, (SELECT COUNT(*) FROM tblName WHERE Cancelled=1) AS Cancelled FROM tblName WHERE 1=1 AND Group>=10)dt
GROUP BY Group, Cancelled
From your example, you don't need the nested query, any recursion, etc...
SELECT
Group,
SUM(Perc) AS total_perc,
SUM(cancelled) AS total_cancelled
FROM
tblName
WHERE
1=1
AND Group >= 10
GROUP BY
Group
If you did have some different data, then you might want to use something like...
SUM(CASE WHEN cancelled > 0 THEN 1 ELSE 0 END) AS total_cancelled

Using Sum() with multiple where clauses

I'm pretty new to this, so forgive if this has been posted (I had no idea what to even search on).
I have 2 tables, Accounts and Usage
AccountID AccountStartDate AccountEndDate
-------------------------------------------
1 12/1/2012 12/1/2013
2 1/1/2013 1/1/2014
UsageId AccountID EstimatedUsage StartDate EndDate
------------------------------------------------------
1 1 10 1/1 1/31
2 1 11 2/1 2/29
3 1 23 3/1 3/31
4 1 23 4/1 4/30
5 1 15 5/1 5/31
6 1 20 6/1 6/30
7 1 15 7/1 7/31
8 1 12 8/1 8/31
9 1 14 9/1 9/30
10 1 21 10/1 10/31
11 1 27 11/1 11/30
12 1 34 12/1 12/31
13 2 13 1/1 1/31
14 2 13 2/1 2/29
15 2 28 3/1 3/31
16 2 29 4/1 4/30
17 2 31 5/1 5/31
18 2 26 6/1 6/30
19 2 43 7/1 7/31
20 2 32 8/1 8/31
21 2 18 9/1 9/30
22 2 20 10/1 10/31
23 2 47 11/1 11/30
24 2 33 12/1 12/31
I'd like to write one query that gives me estimated usage for each month (starting now until the last month that we serve an account) for all accounts being served during that month.
The results would be as follows:
Month-Year Total Est Usage
------------------------------
Oct-12 0 (none being served)
Nov-12 0 (none being served)
Dec-12 34 (only accountid 1 being served)
Jan-13 23 (accountid 1 & 2 being served)
Feb-13 24 (accountid 1 & 2 being served)
Mar-13 51 (accountid 1 & 2 being served)
...
Dec-13 33 (only accountid 2 being served)
Jan-14 0 (none being served)
Feb-14 0 (none being served)
I'm assuming I need to sum and then do a Group By...but not really sure logically how I'd lay this out.
Revised Answer:
I've created a Months table with columns MonthID, Month with values like (201212, 12), (201301, 1), ...
I've also reorganised the usage table to have a month column rather than the start date and end date, as it makes the idea clearer.
See http://sqlfiddle.com/#!3/f57d84/6 for details
The query is now:
Select
m.MonthID,
Sum(u.EstimatedUsage) TotalEstimatedUsage
From
Accounts a
Inner Join
Usage u
On a.AccountID = u.AccountID
Inner Join
Months m
On m.MonthID Between
Year(a.AccountStartDate) * 100 + Month(a.AccountStartDate) And
Year(a.AccountEndDate) * 100 + Month(a.AccountEndDate) And
m.Month = u.Month
Group By
m.MonthID
Order By
1
Previous answer, for reference which assumed usages ranges were full dates rather than just months.
Select
Year(u.StartDate),
Month(u.StartDate),
Sum(Case When a.AccountStartDate <= u.StartDate And a.AccountEndDate >= u.EndDate Then u.EstimatedUsage Else 0 End) TotalEstimatedUsage
From
Accounts a
Inner Join
Usage u
On a.AccountID = u.AccountID
Group By
Year(u.StartDate),
Month(u.StartDate)
Order By
1, 2