SQL select statement filter - tsql

I am struggling with a filter for clients in our system. Each Client has a plan that is carried out monthly. For each plan there are can be multiple visits and for each visit there can be different visit tasks with each task falling under a category e.g.
ClientNo VisitNo VisitTaskID TaskCategory
------------------------------------------
900001 100 19 P
900001 100 18 P
900001 100 01 H
900001 105 21 P
900001 105 19 P
900001 105 16 C
I want to do a count for clients who receive only VisitTaskID 19 for TaskCategory 'P'. I tried using the query below but it will not filter out the other VisitTasks under category P
SELECT COUNT (ClientNo)
FROM Tasks
WHERE VisitTask NOT IN (02,03....18,20,21)
The result still counts clients with VisitTaskID's that I thought I was filtering out.
Each VisitTaskID is unique no matter what category it falls under.
Any help is much appreciated.

Clients who only have task 19 within category p:
SELECT COUNT (ClientNo)
FROM Tasks
WHERE (VisitTask = 19 AND TaskCategory = 'P')
AND NOT EXISTS (SELECT clientno FROM tasks WHERE VisitTask =! 19 AND TaskCategory = 'P')

Related

Select multiple values by category in T-SQL

I have the following table, with over 70K records:
test_1:
ClientID Category
22 Stress
22 Alcohol
22 Scizo
23 Stress
23 Alcohol
24 Stress
24 Scizo
25 Bi Polar
25 Cocaine
25 Meth
26 Stress
I need to SELECT only those ClientIDs, where Category = 'Stress', and also Category = 'Alcohol', within a ClientID.
So, I expect ClientIDs - 22, 23 in my output.
(ClientID 24 has only 'Stress' and no 'Alcohol'; same for ClientID 26, ClientID 25 has no 'Stress' no 'Alcohol'. Means 24, 25, 26 shouldn't be selected)
In this simple code my result includes ClientID = 22, 23, 24, 26. Where 'Stress' appears without 'Alcohol' in last 2 IDs.
SELECT
[ClientID]
,[Category]
FROM
[WH].[dbo].[Test_1]
WHERE
(0=0)
and (Category = 'Stress' or Category = 'Alcohol')
If I write my WHERE statement with AND
WHERE
(0=0)
and (Category = 'Stress' AND Category = 'Alcohol')
then I have no records displayed
Please HELP!
UPD -
Question answered (see below)
Also, if I'd wanted to see the actual categories (not just IDs) in my query, then I do the following:
SELECT
m.[ClientID]
,m.[Category]
FROM
[WH].[dbo].[Test_1] m
INNER JOIN
(
SELECT
[ClientID]
FROM
[WH].[dbo].[Test_1]
WHERE
[Category] IN ('Stress', 'Alcohol')
GROUP BY
[ClientID]
HAVING COUNT(DISTINCT Category) = 2
) cte ON m.ClientID = cte.ClientID
I get the following result:
ClientID Category
22 Stress
22 Alcohol
22 Scizo
23 Stress
23 Alcohol
The problem with your current approach is that the WHERE clause is logic applied to a single record. Instead, you want to perform the category check across multiple records. One approach uses aggregation:
SELECT ClientID
FROM [WH].[dbo].[Test_1]
WHERE Category IN ('Stress', 'Alcohol')
GROUP BY ClientID
HAVING COUNT(DISTINCT Category) = 2;

Book The Art of PostgreSQL self study don't understand lateral join mean?

I guess in this tag many people bought the book "The Art of PostgreSQL". The book content is the context for this question. I self-paced learning from this book. I encountered some problems, so I write a mail to the author, also ask this question in here.
On page 47:
I Totally don't understand what does line 27 limit :n mean?
I also don't know what does line 34 ss(name, albumid, count) on true mean?
I kind of get some part of it. But I am still not sure what does
LATERAL JOIN DO?
-- name: genre-top-n
2 -- Get the N top tracks by genre
3 select genre.name as genre,
4 case when length(ss.name) > 15
5 then substring(ss.name from 1 for 15) || '…'
6 else ss.name
7 end as track,
8 artist.name as artist
9 from genre
10 left join lateral
11 /*
12 * the lateral left join implements a nested loop over
13 * the genres and allows to fetch our Top-N tracks per
Chapter 5 A Small Application | 47
14 * genre, applying the order by desc limit n clause.
15 *
16 * here we choose to weight the tracks by how many
17 * times they appear in a playlist, so we join against
18 * the playlisttrack table and count appearances.
19 */
20 (
21 select track.name, track.albumid, count(playlistid)
22 from track
23 left join playlisttrack using (trackid)
24 where track.genreid = genre.genreid
25 group by track.trackid
26 order by count desc
27 limit :n
28 )
29 /*
30 * the join happens in the subquery's where clause, so
31 * we don't need to add another one at the outer join
32 * level, hence the "on true" spelling.
33 */
34 ss(name, albumid, count) on true
35 join album using(albumid)
36 join artist using(artistid)
37 order by genre.name, ss.count desc;

How do i write a group by query in PostgreSQL

I'm getting errors with PostgreSQL when am writing a group by query,
am sure someone will tell me to put all the columns I've selected in group by, but that will not give me the correct results.
Am writing a query that will select all the vehicles in the database and group the results by vehicles, giving me the total distance and cost for a given period.
Here is how am doing the query.
SELECT i.vehicle AS vehicle,
i.costcenter AS costCenter,
i.department AS department,
SUM(i.quantity) AS liters,
SUM(i.totalcost) AS Totalcost,
v.model AS model,
v.vtype AS vtype
FROM fuelissuances AS i
LEFT JOIN vehicles AS v ON i.vehicle = v.id
WHERE i.dates::text LIKE '%2019-03%' AND i.deleted_at IS NULL
GROUP BY i.vehicle;
If I put all the columns that are in the select in the group bt, the results will not be correct.
How do i go about this without putting all the columns in group by and creating sub-queries?
The fuel table looks like:
vehicle dates department quantity totalcost
1 2019-01-01 102 12 1200
1 2019-01-05 102 15 1500
1 2019-01-13 102 18 1800
1 2019-01-22 102 10 1000
2 2019-01-01 102 12 1260
2 2019-01-05 102 19 1995
2 2019-01-13 102 28 2940
Vehicle Table
id model vtype
1 1 2
2 4 6
2 5 7
This is the results i expect from the query
vehicle dates department quantity totalcost model vtype
1 2019-01-01 102 12 1200 1 2
1 2019-01-05 102 15 1500 1 2
1 2019-01-13 102 18 1800 1 2
1 2019-01-22 102 10 1000 1 2
1 2019-01-18 102 10 1000 1 2
1 65 6500
2 2019-01-01 102 12 1260 5 7
2 2019-01-05 102 19 1995 5 7
2 2019-01-13 102 28 2940 5 7
1 45 6195
Your query doesn't really make sense. Apparently there can be multiple departments and costcenters per vehicle in the fuelissuances table - which of those should be returned?
One way to deal with that, is to return all of them, e.g. as an array:
SELECT i.vehicle,
array_agg(i.costcenter) as costcenters,
array_agg(i.department) as departments,
SUM(i.quantity) AS liters,
SUM(i.totalcost) AS Totalcost,
v.model,
v.vtype
FROM fuelissuances AS i
LEFT JOIN vehicles AS v ON i.vehicle = v.id
WHERE i.dates >= date '2019-03-01'
and i.date < date '2019-04-01'
AND i.deleted_at IS NULL
group by i.vehicle, v.model, v.vtype;
Instead of an array, you could also return a comma separated lists of those values, e.g. string_agg(i.costcenter, ',') as costcenters.
Adding the columns v.model and v.vtype won't (shouldn't) change anything as the group by i.vehicle will only return a single vehicle anyway and thus the model and vtype won't change for that in the group.
Note that I removed the useless aliases and replaced the condition on the date with a proper range condition that can make use of an index on the dates column.
Edit
Based on your new sample data, you want a running total, rather than a "regular" aggregation. This can easily be done using window functions
SELECT i.vehicle,
i.costcenter,
i.department,
SUM(i.quantity) over (w) AS liters,
SUM(i.totalcost) over (w) AS Totalcost,
v.model,
v.vtype
FROM fuelissuances AS i
LEFT JOIN vehicles AS v ON i.vehicle = v.id
WHERE i.dates >= date '2019-01-01'
and i.dates < date '2019-02-01'
AND i.deleted_at IS NULL
window w as (partition by i.vehicle order by i.dates)
order by i.vehicle, i.dates;
I would not create those "total" lines using SQL, but rather in your front end that display the data.
Online example: https://rextester.com/CRJZ27446
You need to use a nested query to get those SUM you want inside that query.
SELECT i.vehicle AS vehicle,
i.costcenter AS costCenter,
i.department AS department,
(SELECT SUM(i.quantity) FROM TABLES WHERE CONDITIONS GROUP BY vehicle) AS liters,
(SELECT SUM(i.totalcost) FROM TABLES WHERE CONDITIONS GROUP BY vehicle) AS Totalcost,
v.model AS model,
v.vtype AS vtype
FROM fuelissuances AS i
LEFT JOIN vehicles AS v ON i.vehicle = v.id
WHERE i.dates::text LIKE '%2019-03%' AND i.deleted_at IS NULL;

Subselect and Max

Alright, I've been trying to conceptualize this for a better part of the afternoon and still cannot figure out how to structure this subselect.
The data that I need to report are ages for a given student major grouped by the past 3 fiscal years. Each fiscal year has 3 semesters (summer, fall, spring). I need to have my query grouped on the fiscalyear and agerange fields and then count the distinct student id's.
I currently have this for my SQL statement:
Select COUNT(distinct StuID), AgeRange, FiscalYear
from tblStatic
where Campus like 'World%' and (enrl_act like 'REG%' or enrl_act like 'SCH%')
and StuMaj = 'LAWSC' and FiscalYear IN ('09/10', '10/11', '11/12')
group by FiscalYear, AgeRange
order by FiscalYear, AgeRange
So this is all fine and dandy except it doesn't match my headcount of students for the fiscalyear. The reason being, that people may cross over in the age ranges during the fiscal year and is adding them to my count twice.
How can I use a subselect to resolve this duplicate entry? The field I have been trying to get working is my semester field and using a max to find the max semester during a fiscalyear for a given student.
Data Sample:
Count AgeRange FiscalYear
3 1 to 19 09/10
20 20 to 23 09/10
60 24 to 29 09/10
96 30 to 39 09/10
34 40 to 49 09/10
14 50 to 59 09/10
3 60+ 09/10
2 1 to 19 10/11
24 20 to 23 10/11
73 24 to 29 10/11
109 30 to 39 10/11
43 40 to 49 10/11
11 50 to 59 10/11
2 60+ 10/11
1 1 to 19 11/12
17 20 to 23 11/12
75 24 to 29 11/12
123 30 to 39 11/12
44 40 to 49 11/12
14 50 to 59 11/12
2 60+ 11/12
Solution: (Just got this working and produced my headcounts that match what they are suppose to be)
Select COUNT(distinct S.StuID), AR.AgeRange, S.FiscalYear
from tblStatic S
INNER JOIN
( Select S.StuID, MIN(AgeRange) as AgeRange
From tblStatic S
Group By S.StuID) AR on S.StuID=AR.StuID
where Campus like 'World%' and (enrl_act like 'REG%' or
enrl_act like 'SCH%')
and StuMaj = 'LAWSC' and FiscalYear IN ('09/10', '10/11', '11/12')
group by S.FiscalYear, AR.AgeRange
order by S.FiscalYear, AR.AgeRange
Replace each student's age range with its maximum (or minimum, if you like) age range that fiscal year, then count them:
;
WITH sourceData AS (
SELECT
StudID,
MaxAgeRangeThisFiscalYear = MAX(AgeRange) OVER
(PARTITION BY StudID, FiscalYear),
FiscalYear
FROM tblStatic
WHERE Campus LIKE 'World%'
AND (enrl_act LIKE 'REG%' OR enrl_act LIKE 'SCH%')
AND StuMaj = 'LAWSC'
AND FiscalYear IN ('09/10', '10/11', '11/12')
)
SELECT
FiscalYear,
AgeRange = MaxAgeRangeThisFiscalYear,
Count = COUNT(DISTINCT StudID)
FROM sourceData
GROUP BY
FiscalYear,
MaxAgeRangeThisFiscalYear
ORDER BY
FiscalYear,
MaxAgeRangeThisFiscalYear

Insert rownumber repeatedly in records in t-sql

I want to insert a row number in a records like counting rows in a specific number of range. example output:
RowNumber ID Name
1 20 a
2 21 b
3 22 c
1 23 d
2 24 e
3 25 f
1 26 g
2 27 h
3 28 i
1 29 j
2 30 k
I rather to try using the rownumber() over (partition by order by column name) but my real records are not containing columns that will count into 1-3 rownumber.
I already try to loop each of record to insert a row count 1-3 but this loop affects the performance of the query. The query will use for the RDL report, that is why as much as possible the performance of the query must be good.
any suggestions are welcome. Thanks
have you tried modulo-ing rownumber()?
SELECT
((row_number() over (order by ID)-1) % 3) +1 as RowNumber
FROM table