Multiple select or distinct - sql

I am new to sql so looking for a little help - got the first part down however I am having issues with the second part.
I got the three tables tied together. First I needed to tie tblPatient.ID = tblPatientVisit.PatientID together to eliminate dups which works
Now I need to take those results and eliminate dups in the MRN but my query is only returning one result which is WRONG - LOL
Query
select
tblPatient.id,
tblPatient.firstname,
tblPatient.lastname,
tblPatient.dob,
tblPatient.mrn,
tblPatientSmokingScreenOrder.SmokeStatus,
tblPatientVisit.VisitNo
from
tblPatient,
tblPatientSmokingScreenOrder,
tblPatientVisit
Where
tblPatient.ID = tblPatientVisit.PatientID
and tblPatientVisit.ID = tblPatientSmokingScreenOrder.VisitID
and tblPatient.ID in(
Select Distinct
tblPatient.mrn
From
tblPatient
where
isdate(DOB) = 1
and Convert(date,DOB) <'12/10/2000'
and tblPatientVisit.PatientType = 'I')
Actual Results:
ID | firstName | LastName | DOB | MRN | SmokeStatus | VisitNO
12 | Test Guy | Today | 12/12/1023 | 0015396 | Never Smoker | 0013957431
Desired Results:
90 | BOB | BUILDER | 02/24/1974 | 0015476 | Former Smoker | 0015476001
77 | DORA | EXPLORER | 06/04/1929 | 0015463 | Never Smoker | 0015463001
76 | MELODY | VALENTINE | 09/17/1954 | 0015461 | Current | 0015461001
32 | STRAWBERRY | SHORTCAKE | 07/06/1945 | 0015415 | Current | 0015415001
32 | STRAWBERRY | SHORTCAKE | 07/06/1945 | 0015415 | Never Smoker | 0015415001
32 | STRAWBERRY | SHORTCAKE | 07/06/1945 | 0015415 | Former Smoker | 0015415001
12 | Test Guy | Today | 12/12/1023 | 0015345 | Never Smoker | 0013957431
Anyone have any suggestions on how I go down to the next level and get all the rows with one unique MRN. From the data above I should have 5 in my list. Any help would be appreciated.
Thanks

If I had to guess -- The only thing that looks odd (but maybe it's OK) is that you're comparing patient.ID from your parent query to patient.mrn in the subquery.
Beyond that -- things to check:
(1)
Do you get all your patients with the inner query?
Select Distinct
tblPatient.mrn
From
tblPatient
where
isdate(DOB) = 1
and Convert(date,DOB) <'12/10/2000'
and tblPatientVisit.PatientType = 'I'
(2)
What is your patient type for the missing records? (Your filtering it to tblPatientVisit.PatientType = 'I' -- do the missing records have that patient type as well?)

Perhaps you need to invert the logic here. As in,
Select Distinct
patients.mrn1
From (select
tblPatient.id as id1,
tblPatient.firstname as firstname1,
tblPatient.lastname as lastname1,
tblPatient.dob as DOB1,
tblPatient.mrn as mrn1,
tblPatientSmokingScreenOrder.SmokeStatus as SmokeStatus1,
tblPatientVisit.VisitNo as VisitNo1,
tblPatientVisit.PatientType as PatientType1,
from
tblPatient,
tblPatientSmokingScreenOrder,
tblPatientVisit
Where
tblPatient.ID = tblPatientVisit.PatientID
and tblPatientVisit.ID = tblPatientSmokingScreenOrder.VisitID
) as patients
where
isdate(patients.DOB1) = 1
and Convert(date,patients.DOB1) <'12/10/2000'
and patients.PatientType1 = 'I');

Cleaned it up a bit and I think they were right. MRN wont match patient id, at least not from your example data. You should not need an inner query. This query should give you what you want.
SELECT DISTINCT
p.id,
p.firstname,
p.lastname,
p.dob,
p.mrn,
s.SmokeStatus,
v.VisitNo
FROM
tblPatient p
JOIN tblPatientVisit v ON p.id = v.patientId
JOIN tblPatientSmokingScreenOrder s ON v.id = s.visitId
WHERE
isdate(p.DOB) = 1
AND CONVERT(date,p.DOB) <'12/10/2000'
AND v.PatientType = 'I'

Related

Condition expected - can't figure out why

I have the following query but it's not working. I want to see only one studentname because dated is unique.
SELECT r.classteacher,
r.studentname,
r.reggroup,
r.yeargroup,
rrl.cdated
FROM [dbo].[Reports] AS r
LEFT OUTER JOIN reportsreadlog AS rrl
ON r.rguid = rrl.creportguid
WHERE
(
SELECT TOP(1) r.studentname
FROM [dbo].[Reports] AS r
LEFT OUTER JOIN reportsreadlog AS rrl
ON r.rguid = rrl.creportguid
WHERE r.periodguid = '4390dc5f-eb21-4673-83f0-e7f973524916'
)
AND r.periodguid = '4390dc5f-eb21-4673-83f0-e7f973524916'
GROUP BY r.classteacher,
r.studentname,
r.reggroup,
r.yeargroup,
rrl.cdated
ORDER BY r.reggroup
Current result when I fix the error:
classteacher | studentname | reggroup | yeargroup | cdate
-------------+-------------+----------+-----------+------
Teacher 1 | Student 1 | class | year | dated
Teacher 1 | Student 1 | class | year | dated
Teacher 1 | Student 1 | class | year | dated
Desired result:
classteacher | studentname | reggroup | yeargroup | cdate
-------------+-------------+----------+-----------+------
Teacher 1 | Student 1 | class | year | dated
Teacher 1 | Student 2 | class | year | dated
Teacher 1 | Student 3 | class | year | dated
Without table definitions and sample/test data, its kind of hard to answer your question. So, please update your post to add them to assist further.
Meanwhile, please try below:
SELECT distinct r.classteacher,
r.studentname,
r.reggroup,
r.yeargroup,
rrl.cdated
FROM [dbo].[Reports] AS r
LEFT OUTER JOIN reportsreadlog AS rrl
ON r.rguid = rrl.creportguid
WHERE r.periodguid = '4390dc5f-eb21-4673-83f0-e7f973524916' --Based on my understanding you don't need other subquery condition here.
-- GROUP BY r.classteacher,
-- r.studentname,
-- r.reggroup,
-- r.yeargroup,
-- rrl.cdated
ORDER BY r.reggroup
Thank you all for helping out. In the end i gave up and did it the ugly way.
Ran the query twice and combined the results
Once and rrl.cdated is null and after that and rrl.cdated is not null

Columns to Rows Two Tables in Cross Apply

Using SQL Server I have two tables, below sample Table #T1 in DB has well over a million rows, Table #T2 has 100 rows. Both tables are in Column format and I need to Pivot to rows and join both.
Can I get it all in one query with Cross Apply and remove the cte?
This is my code, I have correct output but is this the most efficient way to do this considering number of rows?
with cte_sizes
as
(
select SizeRange,Size,ColumnPosition
from #T2
cross apply (
values(Sz1,1),(Sz2,2),(Sz3,3),(Sz4,4)
) X (Size,ColumnPosition)
)
select a.ProductID,a.SizeRange,c.Size,isnull(x.Qty,0) as Qty
from #T1 a
cross apply (
values(a.Sale1,1),(a.Sale2,2),(a.Sale3,3),(a.Sale4,4)
) X (Qty,ColumnPosition)
inner join cte_sizes c
on c.SizeRange = a.SizeRange
and c.ColumnPosition = x.ColumnPosition
I have also code and considered this but is this the CROSS APPLY a better method?
with cte_sizes
as
(
select 1 as SizePos
union all
select SizePos + 1 as SizePos
from cte_sizes
where SizePos < 4
)
select a.ProductID
,a.SizeRange
,(case when b.SizePos = 1 then c.Sz1
when b.SizePos = 2 then c.Sz2
when b.SizePos = 3 then c.Sz3
when b.SizePos = 4 then c.Sz4 end
) as Size
,isnull((case when b.SizePos = 1 then a.Sale1
when b.SizePos = 2 then a.Sale2
when b.SizePos = 3 then a.Sale3
when b.SizePos = 4 then a.Sale4 end
),0) as Qty
from #T1 a
inner join #T2 c on c.SizeRange = a.SizeRange
cross join cte_sizes b
This is wild guessing, but my magic crystall ball told me, that you might be looking for something like this:
For this we do not need your table #TS at all.
WITH Unpivoted2 AS
(
SELECT t2.SizeRange,A.* FROM #t2 t2
CROSS APPLY(VALUES(1,t2.Sz1)
,(2,t2.Sz2)
,(3,t2.Sz3)
,(4,t2.Sz4)) A(SizePos,Size)
)
SELECT t1.ProductID
,Unpivoted2.SizeRange
,Unpivoted2.Size
,Unpivoted1.Qty
FROM #t1 t1
CROSS APPLY(VALUES(1,t1.Sale1)
,(2,t1.Sale2)
,(3,t1.Sale3)
,(4,t1.Sale4)) Unpivoted1(SizePos,Qty)
LEFT JOIN Unpivoted2 ON Unpivoted1.SizePos=Unpivoted2.SizePos AND t1.SizeRange=Unpivoted2.SizeRange
ORDER BY t1.ProductID,Unpivoted2.SizeRange;
The result:
+-----------+-----------+------+------+
| ProductID | SizeRange | Size | Qty |
+-----------+-----------+------+------+
| 123 | S-XL | S | 1 |
+-----------+-----------+------+------+
| 123 | S-XL | M | 12 |
+-----------+-----------+------+------+
| 123 | S-XL | L | 13 |
+-----------+-----------+------+------+
| 123 | S-XL | XL | 14 |
+-----------+-----------+------+------+
| 456 | 8-14 | 8 | 2 |
+-----------+-----------+------+------+
| 456 | 8-14 | 10 | 22 |
+-----------+-----------+------+------+
| 456 | 8-14 | 12 | NULL |
+-----------+-----------+------+------+
| 456 | 8-14 | 14 | 24 |
+-----------+-----------+------+------+
| 789 | S-L | S | 3 |
+-----------+-----------+------+------+
| 789 | S-L | M | NULL |
+-----------+-----------+------+------+
| 789 | S-L | L | 33 |
+-----------+-----------+------+------+
| 789 | S-L | XL | NULL |
+-----------+-----------+------+------+
The idea in short:
The cte will return your #T2 in an unpivoted structure. Each name-numbered column (something you should avoid) is return as a single row with an index indicating the position.
The SELECT will do the same with #T1 and join the cte against this set.
UPDATE: After a lot of comments...
If I get this (and the changes to the initial question) correctly, the approach above works perfectly well, but you want to know, what was best in performance.
The first answer to "What is the fastest approach?" is Race your horses by Eric Lippert.
Good to know 1: A CTE is nothing more then syntactic sugar. It will allow to type a sub-query once and use it like a table, but it has no effect to the way how the engine will work this down.
Good to know 2: It is a huge difference whether you use APPLY or JOIN. The first will call the sub-source once per row, using the current row's values. The second will have to create two sets first and will then join them by some condition. There is no general "what is better"...
For your issue: As there is one very big set and one very small set, all depends on when you reduce the big set usig any kind of filter. The earlier the better.
And most important: It is - in any case - a sign of bad structures - when you find name numbering (something like phone1, phone2, phoneX). The most expensive work will be to transform your 4 name-numbered columns to some dedicated rows. This should be stored in normalized format...
If you still need help, I'd ask you to start a new question.

PostgreSQL searching the query result based on defined conditions

I am a beginner and I am working on a quite big query which returns about 50k of rows.
I've spent a big number of hours trying to figure out the issue and it looks that there is a gap in my knowledge and I would be really greatful if you could help me.
In order to show you the main idea I decided to simplify and split the data. I am presenting the relevant tables here:
*company*
+----+----------+---------------+----------------+
| ID | name | classificaton | special_number |
+----+----------+---------------+----------------+
| 1 | companyX | 309 | 242 |
+----+----------+---------------+----------------+
*branch*
+----+---------------+-------+
| ID | name | color |
+----+---------------+-------+
| 1 | environmental | green |
| 2 | navy | blue |
+----+---------------+-------+
*company_branch*
+------------+-----------+
| ID_company | ID_branch |
+------------+-----------+
| 1 | 1 |
| 1 | 2 |
+------------+-----------+
Ok as we have all the needed data presented I need to create a query which will select all the companies along with the main color of the branches they are working in.
A companyX can belong to more than one branch but I need to show only the main branch which can be calculated based on the three conditions below:
*if classification = 309 and special_number is even then show the relevant color and go the next company (ignore the next conditions)
*if classification = 209 and special_number is even then show the relevant color and go the next company (ignore the next condition)
*else show as grey
I created a query like that: (I know that the case phrase is not correct but I am keeping it as it shows in a better way what I am trying to accomplish)
SELECT c.ID, c.name, b.color, c.classification, c.special_number,
CASE
WHEN c.classification = 309 AND c.special_number % 2 = 0 THEN b.color
WHEN c.classification = 209 AND c.special_number % 2 = 0 THEN b.color
ELSE 'grey'
END AS 'case'
FROM company c INNER JOIN company_branch cb ON c.ID = cb.ID_company
INNER JOIN branch b ON b.ID = cb.ID_branch
then I get the following result
*result*
+----+----------+-------+----------------+----------------+------+
| ID | name | color | classification | special_number | case |
+----+----------+-------+----------------+----------------+------+
| 1 | companyX | green | 309 | 242 | green|
| 1 | companyX | blue | 309 | 242 | blue |
+----+----------+-------+----------------+----------------+------+
The problem is that if any company belongs to more than one branch then I always get many colors... what I would like to get is a list of companies with only one color of the main branch they are working in.
can you guys help the newbie ?
I think you want something like this:
SELECT DISTINCT ON (c.id) c.ID, c.name,
COALESCE(b.color, 'grey') as color
c.classification, c.special_number,
FROM company c LEFT JOIN
company_branch cb
ON c.ID = cb.ID_company LEFT JOIN
branch b
ON b.ID = cb.ID_branch
ORDER BY c.Id,
(CASE WHEN c.classification = 309 AND c.special_number % 2 = 0 THEN 1
WHEN c.classification = 209 AND c.special_number % 2 = 0 THEN 2
ELSE 3
END);
DISTINCT ON is a (useful) Postgres extension that returns one row per item(s) in parentheses. The returned row is based on the GROUP BY. The first key(s) in the ORDER BY need to be the item(s). The following specifies what "first" means.
I switched the joins to being outer joins, so all companies are in the result set.

Select and count in the same query on two tables

I've got these two tables:
___Subscriptions
|--------|--------------------|--------------|
| SUB_Id | SUB_HotelId | SUB_PlanName |
|--------|--------------------|--------------|
| 1 | cus_AjGG401e9a840D | Free |
|--------|--------------------|--------------|
___Rooms
|--------|-------------------|
| ROO_Id | ROO_HotelId |
|--------|-------------------|
| 1 |cus_AjGG401e9a840D |
| 2 |cus_AjGG401e9a840D |
| 3 |cus_AjGG401e9a840D |
| 4 |cus_AjGG401e9a840D |
|--------|-------------------|
I'd like to select the SUB_PlanName and count the rooms with the same HotelId.
So I tried:
SELECT COUNT(*) as 'ROO_Count', SUB_PlanName
FROM ___Rooms
JOIN ___Subscriptions
ON ___Subscriptions.SUB_HotelId = ___Rooms.ROO_HotelId
WHERE ROO_HotelId = 'cus_AjGG401e9a840D'
and
SELECT
SUB_PlanName,
(
SELECT Count(ROO_Id)
FROM ___Rooms
Where ___Rooms.ROO_HotelId = ___Subscriptions.SUB_HotelId
) as ROO_Count
FROM ___Subscriptions
WHERE SUB_HotelId = 'cus_AjGG401e9a840D'
But I get empty datas.
Could you please help ?
Thanks.
You need to use GROUP BY whenever you do some aggregation(here COUNT()). Below query will give you the number of ROO_ID only for the SUB_HotelId = 'cus_AjGG401e9a840D' because you have this condition in WHERE. If you want the COUNTs for all Hotel_IDs then you can simply remove the WHERE filter from this query.
SELECT s.SUB_PlanName, COUNT(*) as 'ROO_Count'
FROM ___Rooms r
JOIN ___Subscriptions s
ON s.SUB_HotelId = r.ROO_HotelId
WHERE r.ROO_HotelId = 'cus_AjGG401e9a840D'
GROUP BY s.SUB_PlanName;
To be safe, you can also use COUNT(DISTINCT r.ROO_Id) if you don't want to double count a repeating ROO_Id. But your table structures seem to have unique(non-repeating) ROO_Ids so using a COUNT(*) should work as well.

Count in SQL server

select
hotel.hotel_name 'Hotel Name',
Address 'Address',
Description 'Description',
Contact_No 'Contact Number'
from hotel, customer_profile, booking_details where
customer_profile.email = booking_details.email
and hotel.hotel_name = booking_details.hotel_name
and booking_details.email = 'davidho#yahoo.com'
order by 1
I can't add images /:
So for example currently my result is
+-----------------------+
| Name | Address |
+-----------------------+
| Hotel1 | Beach Road1 |
| Hotel2 | Beach Road2 |
| Hotel2 | Beach Road2 |
+-----------------------+
I want it to remove the duplicates, and add a new column "Number of times" which indicate how many times it appeared.
I want it to be
+------------------------------------+
| Name | Address | No of Times |
+------------------------------------+
| Hotel1 | Beach Road1 | 1 |
| Hotel2 | Beach Road2 | 2 |
+------------------------------------+
You should use for your examples group by clause a below
select
hotel.hotel_name "Hotel Name",
Address, count(1) "No of Times"
from hotel, customer_profile, booking_details where
customer_profile.email = booking_details.email
and hotel.hotel_name = booking_details.hotel_name
and booking_details.email = 'davidho#yahoo.com'
group by hotel.hotel_name, Address
order by 1
First, learn proper join syntax, don't use single quote for column names, and use column aliases for all columns. Here is what I think your current query is doing.
select h.hotel_name as HotelName, h.Address. h.Description
cp.Contact_No
from booking_details bd join
hotel h
on h.hotel_name = bd.hotel_name join
customer_profile cp
on cp.email = bd.email
where bd.email = 'davidho#yahoo.com'
order by 1;
Next, to solve your question, just use group by. You can also eliminate the join to customer_profile, because I don't think that table gets used:
select h.hotel_name as HotelName, h.Address. count(*) as NumberOfTimes
from booking_details bd join
hotel h
on h.hotel_name = bd.hotel_name
where bd.email = 'davidho#yahoo.com'
order by 1;