Complex SQL Query of Group BY - sql

I have 4 Tables ( Cattle , Farm , Vaccine , vReminder )
I want to select from Vaccine Reminder table all reminders to be shown as
( 2 ( Cows ) have ( Vaccine Name 1) from ( Farm Name 1) on this Date )
( 3 ( Horses) have ( Vaccine Name 1) from ( Farm Name 1) on this Date )
( 1 ( Cow) have ( Vaccine Name 5) from ( Farm Name 2) on this Date ) and so On
I wrote this Query But it is not Grouping the same Vaccine Type that are located for the same Farm if the cattle is of the same type :
My Query :
SELECT
(Select v.vName from vaccine as v where v.vID= vr.[vID]),
vr.[Type],
vsD,
COUNT(vr.[Type]),
f.farmName
FROM [QNFARM].[dbo].[vReminder] as vr inner join
[cattle] as c on c.RFID = vr.RFID inner join
farm as f on f.farmID = c.fID
Group by
vr.vID,
vr.Type,
vsD,
vr.RFID,
f.farmName
But as you See in the screen shot that it is Vname Pinkeye for to same breeds and same Dates and same Farm it does not Group Them .

The Problem that I was grouping also by RFID of the cattle that was preventing the group function to collect the Same vaccine id , so the correct query is :
SELECT f.farmName,vr.[Type],
(Select v.vName from vaccine as v where v.vID= vr.[vID]) as vName,vr.vsD
,COUNT(*) as Num
FROM [QNFARM].[dbo].[vReminder] as vr inner join [cattle] as c on c.RFID=vr.RFID inner join farm as f on f.farmID=c.fID Group by f.farmName,vr.Type,vr.vID,vr.vsD
I just remove the RFID from the group by statement and the result now

Don't use a subquery to get the vaccine name. Just use joins:
SELECT v.vName, vr.[Type], vsD, COUNT(*), f.farmName
FROM [QNFARM].[dbo].[vReminder] vr INNER JOIN
[cattle] c
ON c.RFID = vr.RFID INNER JOIN
farm f
ON f.farmID = c.fID INNER JOIN
vaccine v
ON v.vid = vr.vid
GROUP BY v.vname, vr.Type, vsD, f.farmName;

Related

How to join the subquery result(having nested joins ) with another table?

I am implementing nested join statement using sub-queries. I am able to get the result for 3 tables but unable to join it further for four or n tables .
SELECT X.personCityId
, X.name
, X.age
, X.cityId
, X.state
, Y.age
, Y.name
FROM (SELECT Person.cityId AS personCityId
, Person.name
, Person.age
, CityDetails.cityId AS cityId
, CityDetails.state
FROM Person
LEFT JOIN CityDetails
ON Person.cityid = CityDetails.cityid ) AS X
LEFT JOIN (SELECT name , age, lastname FROM PersonalDetails) AS Y
ON X.age=Y.age
Let say if i have to join result of above query with 4th table by left join on age column
LEFT JOIN (SELECT age,height, weight from PersonMedicalRecords ) as D ON Z.age = a.age
I want to know how this can be done through sub-queries only.
You can use "common table expression" to achieve this and maintain level wise hierarchy
Like this
;
WITH cte AS (
SELECT Person.cityId AS personCityId
, Person.name
, Person.age
, CityDetails.cityId AS cityId
, CityDetails.state
FROM Person
LEFT JOIN CityDetails
ON Person.cityid = CityDetails.cityid )
, ct AS (
SELECT * ----- here on * use your required column
FROM (SELECT * ----- here on * use your required column
FROM cte) AS x
LEFT JOIN (SELECT name
, age
, lastname
FROM PersonalDetails) as y
ON X.age=y.age )
, ct2 AS (
SELECT * ----- here on * use your required column
FROM (SELECT * ----- here on * use your required column
FROM ct) as y
LEFT JOIN (SELECT height
, weight
, age ---- added from our side considering it is available for implementing join on age
FROM PersonMedicalRecords) as z
ON y.age=z.age )
SELECT * FROM ct2 ----- here on * use your required column
Here output of one left join will take as input table with next join and similarly you can maintain your hierarchy of join.
You are unnecessarily nesting your queries. You should write your query as:
SELECT p.cityId as personCityId, p.name, p.age,
cd.cityId, cd.state,
pd.age, pd.name
FROM Person p LEFT JOIN
CityDetails cd
ON p.cityid = cd.cityid LEFT JOIN
PersonalDetails pd
ON pd.age = p.age;
Note the use of meaningful table aliases (abbreviations for the table names) rather than arbitrary letters. Also, no subqueries.
If you want to add another JOIN, just add another JOIN, following the pattern of the other joins.
**I tried this and it works
SELECT X.personCityId, X.name, X.age, X.cityId, X.state, Y.age FROM (SELECT Person.cityId AS personCityId, Person.name, Person.age, CityDetails.cityId AS cityId, CityDetails.state FROM Person LEFT JOIN CityDetails ON Person.cityid = CityDetails.cityid) AS X LEFT JOIN (SELECT age, lastname FROM PersonalDetails) AS Y ON X.age=Y.age LEFT JOIN (SELECT age, height, weight FROM PersonMedicalRecords)AS Z ON Y.age = Z.age

SELECT common entities only based on different corresponding entities

3 Tables
Client -
CID Name
1 Ana
2 Bana
3 Cana
ClientProgram (Bridge Table) -
CID PID
1 4
1 5
1 8
2 10
Program -
PID Program
4 X
5 Y
8 Z
10 G
Desired Output:
Name Program
Ana X
Ana Y
I want to extract only those Clients which are common/exist in different Programs I choose (say X and Y in this case)
Query attempt:
SELECT
C.Name
,P.Program
FROM ClientProgram CP
INNER JOIN Client C
ON CP.CID=C.CID
INNER JOIN Program P
ON CP.PID=P.PID
INNER JOIN ClientProgram CP1
ON CP.CID=CP1.CID
WHERE P.Program = 'X' OR P.Program = 'Y'
AND CP.CID = CP1.CID
This however doesn't pulls in all clients and not only those which exist in multiple programs.
;WITH cte AS (
SELECT
c.Name
,p.Program
,COUNT(*) OVER (PARTITION BY c.CID) as ProgramCount
FROM
Program p
INNER JOIN ClientProgram cp
ON p.PID = cp.PID
INNER JOIN Client c
On cp.CID = c.CID
WHERE
p.Program IN ('X','Y')
)
SELECT Name, Program
FROM
cte
WHERE
ProgramCount > 1
The use of COUNT(*) over will be a problem if PID is not unique in Programs or if the combination of CID to PID in ClientProgram is not unique. However I would assume uniqueness based on what I see.
If not you can go a route like this:
;WITH cte AS (
SELECT
cp.CID
FROM
Program p
INNER JOIN ClientProgram cp
ON p.PID = cp.PID
WHERE
p.Program IN ('X','Y')
GROUP BY
cp.CID
HAVING
COUNT(DISTINCT p.PID) > 1
)
SELECT
c.Name
,p.Program
FROM
cte t
INNER JOIN Client c
ON t.CID = c.CID
INNER JOIN ClientProgram cp
ON t.CID = cp.CID
INNER JOIN Program p
ON cp.PID = p.PID
AND p.Program IN ('X','Y')
This is kind of a round about way of doing it. Probably a better way but this will do it. I through in scripts for temp table in case someone else wants to improve. Could do a temp table for example instead of CTE.
create table #client(cid int,name varchar(20))
create table #clientprogram (cid int, pid int)
create table #program( pid int, program varchar(20))
insert into #client
values(1,'Ana')
,(2,'Bana')
,(3,'Cana')
insert into #clientprogram
values (1,4)
,(1,5)
,(1,8)
,(2,10)
,(2,4)
insert into #program
values (4,'x')
,(5,'y')
,(8,'z')
,(10,'g')
WITH CHECKPLEASE AS(
Select c.Name,ISNULL(p.Program,p2.PRogram) Program
from #client c
inner join #clientprogram cp
on c.CID = cp.CID
left join #program p
on cp.PID = p.PID
and p.PRogram = 'X'
left join #program p2
on cp.PID = p2.PID
and p2.Program = 'Y'
where ISNULL(p.Program,p2.PRogram) is not null
)
Select *
From CHECKPLEASE
where Name in (
SELECT Name
From CHECKPLEASE
group by Name
having COUNT(*) > 1)

sql query- group by and then join

I have 2 tables as follow:
1)passenger - with passenger_id,passenger_name and passenger_city
2)flight - with flight_id,flight_name and passenger_id.
The question is:
List the passenger details with flight id, who has travelled in more than one flight.
(This function will display the passenger details with their flight id's who has travelled in more than one flight.)
I used this query:
select * from passenger_1038299
where passengerid in(select passengerid from flight_1038299
group by passengerid
having count(passengerid)>1);
but it doesnt give me flight_ids. please tell how to retrieve flight id as well.
thanks and sorry for stupid question as new to sql.
Join the flight table to get the passenger's flights
select * from passenger_1038299 p
join flight_1038299 f on f.passenger_id = p.passenger_id
where p.passengerid in(
select passengerid from flight_1038299 group by passengerid having count(passengerid)>1
);
I like to use exists to check for multiples. With an index on passenger_id it may run faster than the query above.
select * from passenger_1038299 p
join flight_1038299 f on f.passenger_id = p.passenger_id
where exists (
select 1 from flight_1038299 f2
where f2.passenger_id = f.passenger_id
and f2.flight_id <> f.flight_id
)
Edit
Another way using the count window function:
select * from (
select *,
count() over (partition by p.passenger_id) cnt
from passenger_1038299 p
join flight_1038299 f on f.passenger_id = p.passenger_id
) t where cnt > 1
Another way with using analytic functions:
SELECT * FROM (
SELECT p.*, f.flight_id,
count(*) OVER (PARTITION BY f.passenger_id ) As number_of_flights
FROM passenger p
JOIN flight f
ON p.passenger_id = f.passenger_id
)
WHERE number_of_flights > 1
Demo: http://sqlfiddle.com/#!4/dab21/11
Try this way
Flight id should be multiple so it comma separated of column.
select a.*,b.flightid from passenger_1038299 a
join (select passengerid,Stuff((SELECT ', ' + s.flight_id
FROM flight_1038299 l
where c.passengerid = l.passengerid
FOR XML PATH('')),1,1,'') flightid from flight_1038299 as c group by c.passengerid having count(c.passengerid)>1) b on a.passengerid=b.passengerid

Find projects which belong to several categories

I have a projects table (columns id and name), a categories table (columns id and name) and a join table projects_categories (columns project_id and category_id).
I want to write a query that returns all the projects which belong to a set of categories. For instance if I have
projects
id name
1 foo
2 bar
categories
id name
1 bee
2 gee
projects_categories
project_id category_id
1 1
1 2
2 1
I want to write a query that returns me the project "foo" if I pass the category ids 1 and 2. I have tried the following query, but it it returns all the projects that belongs to category 1 OR 2, not category 1 AND 2.
SELECT "projects".*
FROM "projects"
INNER JOIN "projects_categories" ON "projects_categories"."project_id" = "project"."id"
WHERE "projects_categories"."category_id" IN (1, 2)
The following query does not return any result:
SELECT "projects".*
FROM "projects"
INNER JOIN "projects_categories" ON "projects_categories"."project_id" = "project"."id"
WHERE "projects_categories"."category_id" = 1
AND "projects_categories"."category_id" = 2
I understand why these queries return these results, but can't figure out how to write the query I need.
You can try with EXISTS :
SELECT "p".*
FROM "projects" "p"
WHERE EXISTS (
SELECT *
FROM "projects_categories" "pc"
WHERE "pc"."category_id" IN (1, 2)
AND "pc"."project_id" = "p"."id"
GROUP BY "pc"."project_id"
HAVING COUNT(DISTINCT "pc"."project_id" ) = 2)
)
Or JOIN :
SELECT "p".*
FROM "projects" "p"
JOIN (
SELECT "pc"."project_id"
FROM "projects_categories" "pc"
WHERE "pc"."category_id" IN (1, 2)
GROUP BY "pc"."project_id"
HAVING COUNT(DISTINCT "pc"."project_id" ) = 2)
) "tpc" ON "tpc"."project_id" = "p"."id"
Or IN :
SELECT "p".*
FROM "projects" "p"
WHERE "p"."id" IN (
SELECT "pc"."project_id"
FROM "projects_categories" "pc"
WHERE "pc"."category_id" IN (1, 2)
GROUP BY "pc"."project_id"
HAVING COUNT(DISTINCT "pc"."project_id" ) = 2)
)
The issue is that the query was looking into the same table and column and attempting to have two different values. This will separate the two different conditions, and then combine them into one result set.
SELECT A.ProjectName, A.CategoryId, B.CategoryId
FROM
(SELECT P.Name [ProjectName], PC.CategoryId, PC.ProjectId
FROM #Projects P
INNER JOIN #Project_Categories PC
ON PC.ProjectId = P.ID
WHERE PC.CategoryId = 1
) A
INNER JOIN (SELECT P.Name [ProjectName], PC.CategoryId, PC.ProjectId
FROM #Projects P
INNER JOIN #Project_Categories PC
ON PC.ProjectId = P.ID
WHERE PC.CategoryId = 2
) B
ON A.ProjectId = B.ProjectId

SQL - UNION, priority on the first select statement when doing order by

I'm trying to print out the results from the "GermanDB" Database first, while also showing everything from the Boston DB that was not in the German database. Can this be done in one query?
My query (the bold part functions but does not order the way I want)
select * from (
SELECT DISTINCT a.ProductRef
FROM GERMANDB.dbo.LOCATIONS AS a INNER JOIN GERMANDB.dbo.ITEMS AS b ON a.ProductRef = b.ProductRef
WHERE b.ACTIVE=1
) ta
UNION select * from
SELECT DISTINCT c.ProductRef
FROM BOSTONDB.dbo.LOCATIONS AS c INNER JOIN BOSTONDB.dbo.ITEMS AS d ON c.ProductRef = d.ProductRef
WHERE c.ACTIVE=1 (c.ProductRef NOT IN
(SELECT ProductRef FROM GERMANDB.dbo.ITEMS where ACTIVE=1))
) tb
order by ta.ProductRef** , tb.productRef
Just add one field to signal the priority. Like this:
select *, 0 as Priority from (
SELECT DISTINCT a.ProductRef
FROM GERMANDB.dbo.LOCATIONS AS a INNER JOIN GERMANDB.dbo.ITEMS AS b ON a.ProductRef = b.ProductRef
WHERE b.ACTIVE=1
) ta
UNION select *, 1 as Priority from
SELECT DISTINCT c.ProductRef
FROM BOSTONDB.dbo.LOCATIONS AS c INNER JOIN BOSTONDB.dbo.ITEMS AS d ON c.ProductRef = d.ProductRef
WHERE c.ACTIVE=1 (c.ProductRef NOT IN
(SELECT ProductRef FROM GERMANDB.dbo.ITEMS where ACTIVE=1))
) tb
order by Priority, ProductRef