Need assistance with aggregate Count function - sql

I have been doing nice little project to calculate the number of shipments per country made under particular office. I prefer to write a single query because otherwise it will take forever to process whole the data using while / for loops etc.
For this reason I wrote a query as following:
SELECT
COUNTRY.Code, COUNTRY.Name,
(SELECT COUNT(Shipment.Ref) FROM Shipment
LEFT JOIN Station (NOLOCK) ON Station.STID = Shipment.STDEST
LEFT JOIN Country (NOLOCK) ON Station.Code = Country.Code
LEFT JOIN Client (NOLOCK) ON Client.CID = Shipment.CID
WHERE Client.CBUR = 'UKLON'
) AS NumOrder
FROM COUNTRY
And this return a total number of shipments and not per particular country
--------------------------------------
|Code |Name |NumOrder|
--------------------------------------
| BE |BELGIUM | 82460 |
| FR |FRANCE | 82460 |
| UK |UNITED KINGDOM | 82460 |
| NL |NETHERLANDS | 82460 |
| AZ |AZERBAIJAN | 82460 |
| HK |HONG KONG | 82460 |
| AU |AUSTRALIA | 82460 |
| RE |REUNION ISLAND | 82460 |
I really appreciate if anyone would give me a hint how to modify the existing query to be able to count those shipment per country based on the given office account .

Try adding the Country in the Count
SELECT
COUNTRY.Code, COUNTRY.Name,
(SELECT COUNT(Shipment.Ref) FROM Shipment
LEFT JOIN Station (NOLOCK) ON Station.STID = Shipment.STDEST
LEFT JOIN Country (NOLOCK) ON Station.Code = TPAY.Code
LEFT JOIN Client (NOLOCK) ON Client.CID = Shipment.CID
WHERE Client.CBUR = 'UKLON' and Country.Name = C.Name --or this "and Country.Code = C.Code"
) AS NumOrder
FROM COUNTRY C

Smth. like this, in one request, consider group by clause:
SELECT
COUNTRY.Code, COUNTRY.Name, COUNT(Shipment.Ref)
FROM Country
-- joins go here: Shipment etc.
GROUP BY COUNTRY.Code, COUNTRY.Name

Change it to:
SELECT
C.Code, C.Name,
(
SELECT COUNT(Shipment.Ref) FROM Shipment
LEFT JOIN Station (NOLOCK) ON Station.STID = Shipment.STDEST
LEFT JOIN Client (NOLOCK) ON Client.CID = Shipment.CID
WHERE Client.CBUR = 'UKLON' AND Station.Code=C.Code
) AS NumOrder
FROM COUNTRY C
OR Use Group By:
SELECT
C.Code, C.Name,
COUNT(Shipment.Ref) AS NumOrder
FROM Shipment
LEFT JOIN Station (NOLOCK) ON Station.STID = Shipment.STDEST
LEFT JOIN Client (NOLOCK) ON Client.CID = Shipment.CID
LEFT JOIN COUNTRY C ON Station.Code=C.Code
WHERE Client.CBUR = 'UKLON'
GROUP BY C.Code

These are my assumptions:
STID is Station's primary key.
CID is Client's primary key.
TPAY is supposed to be Country.
Code is Country's primary key.
So your left joins return one record anyway (the matching one or a dummy empty one). Outer joins make no sense in your query.
You are not correlating your subquery to your main query, i.e. you are not selecting records within for the country without. You'd have to add this to your WHERE clause in your subquery.
SELECT
COUNTRY.Code,
COUNTRY.Name,
(
SELECT COUNT(*)
FROM Shipment
JOIN Station ON Station.STID = Shipment.STDEST
WHERE Station.Code = COUNTRY.Code
AND Shipment.CID IN (SELECT CID FROM Client WHERE CBUR = 'UKLON')
) AS NumOrder
FROM COUNTRY;
And here is the same thing with the subquery in the FROM clause instead:
SELECT
COUNTRY.Code,
COUNTRY.Name,
counted.NumOrder
FROM COUNTRY
JOIN
(
SELECT Station.Code, COUNT(*) AS NumOrder
FROM Station
JOIN Shipment ON Shipment.STDEST = Station.STID
AND Shipment.CID IN (SELECT CID FROM Client WHERE CBUR = 'UKLON')
GROUP BY Station.Code
) counted ON counted.Code = COUNTRY.Code;

Related

GROUP BY Subquery returns more than one row

I'm looking for a way to solve the following situation. I have a table that I need to return only one number for each "p.pays", This query is supposed to list "nom from table Pays" where at least half of the "athlete" have are in the table "Resultat" but my subquery returns more than one line is there a way I can match "p.code" in both the query and the subquery so it only returns 1 line per "p.code".
SELECT p.nom , count(*) FROM Athlete a
INNER JOIN Pays p ON a.pays = p.code
GROUP BY p.code HAVING count(*)/2 >= (SELECT count(*) FROM Athlete a
INNER JOIN Pays p ON a.pays = p.code
INNER JOIN Resultat r ON a.code = r.athlete
GROUP BY p.code);
Expected result, show Countries"Pays" where at least half of the athletes "Athlete" have won a medal (Athlete is in the Resultat table). :
p.nom | count(*)
|----------|--------|
|Albania | 134 | <-- Total Number of athletes "Athlete" in the
|Argentina | 203 | country "Pays".
| ... | ... |
You want to have two counts of athlethes in the country:
all athletes
the resultat athletes
Use a conditional count for this:
SELECT p.nom, count(*)
FROM pays p
INNER JOIN athlete a ON a.pays = p.code
GROUP BY p.code
HAVING COUNT(*) / 2 >=
COUNT(*) FILTER (WHERE a.code IN (SELECT athlete FROM resultat))
ORDER BY p.nom;

self join after an inner join

I am finding what cities have the same name in different states. The city name and state name are in seperate tables (cities and states) and can be inner joined over a seperate common column.
select c1.city, c1.state, c2.city, c2.state
from cities
inner join states on cities.commonid = states.commonid
After inner joining i need to self join to perform a function as follows
select c1.city, c1.state, c2.city, c2.state
from *joined table* c1 join
*joined table* c2
on c1.city = c2.city and c1.state <> c2.state
i am wondering how i can self join a table that is the result of another join in the same query
output will be like this
+----------+-------+--------+--------+
| city1 | state1|city2 |state2 |
+----------+-------+--------+--------+
| x | melb | x | syd |
| y | bris | y | ACT |
+----------+-------+--------+--------+
I assume that the table cities has a column like state_id that references a column state_id in the table states (change the names to the actual names of the columns).
First do a self join for cities with the conditions:
c1.city = c2.city AND c1.state_id < c2.state_id
The < operator makes sure that each pair of cities wil be returned only once.
Then join 2 copies of states, because each of them will be used to get the name of the state for each of the 2 cities:
SELECT c1.city city1, s1.state state1,
c2.city city2, s2.state state2
FROM cities c1
INNER JOIN cities c2 ON c1.city = c2.city AND c1.state_id < c2.state_id
INNER JOIN states s1 ON s1.state_id = c1.state_id
INNER JOIN states s2 ON s2.state_id = c2.state_id
ORDER BY city1
You can do select of your inner join query and give it alias.
Then it will become something like below...
Select c.city,c.state from
(Select City,state from cities inner join states where cities.id = states.id) as c
Now make a self join for c.
I would perform a pre-query of all cities that have more than one state. Then join to the states table to see which states they are encountered.
select
Duplicates.city,
s.state
from
( select c1.city
from cities c1
group by c1.city
having count(*) > 1 ) Duplicates
JOIN cities c2
on Duplicates.city = c2.city
JOIN states s
on c2.commonid = s.commonid
order by
Duplicates.city,
s.state
By NOT trying to do cross-tab of just two city/states, you would get a single list. If one city name exists in 5 states, how would you plan on showing that. This way you would see all alphabetized.
I would suggest a CTE:
with cs as (
select c.name as city_name, s.name as state_name
from cities c join
states s
on c.commonid = s.commonid
)
select cs1.*, cs2.*
from cs cs1 join
cs cs2
on cs1.name = cs2.name and cs1.state <> cs2.state;

SQL query to find a record which has all matching records in another table

I have below 3 tables and I want to write a SQL query which will list the store present in all city: (here the result should be "Walmart")
Stores:
ID Name
1 Walmart
2 Target
3 Sears
Stores_City
ID Store_id City ID
1 1 10
2 1 20
3 2 10
4 1 30
City
ID Name
10 NewYork
20 Boston
30 Eagan
I am unable to find a query that works. Any help is appreciated!
select s.Name
from Stores s
inner join
(
select store_id, count(distinct city_id)
from stores_city
group by store_id
having count(distinct city_id) = (select count(*) from City)
) x
on x.store_id = s.id;
You can do it by grouping on store_id and checking for the count from stores table.
A straight join would work
Select distinct s.name from stores s inner join store _city SC on s.id=sc.id
Inner join city c on
Sc.city_id = c.id
Here is another way that will work:
select s.*
from stores s
where not exists (
select c.id
from city c
except
select sc.city_id
from stores_city sc
where sc.store_id = s.id
)
Try this:
SELECT
s.Name
FROM Stores s
WHERE NOT EXISTS (SELECT TOP 1
1
FROM City c
LEFT JOIN Stores_City sc
ON c.ID = sc.CityID
AND sc.Store_id = s.ID
WHERE sc.ID IS NULL)

SQL troubling with multiple count in query

I already provide sql fiddle with schema and sample data.
http://sqlfiddle.com/#!2/e9d22/7/0
If I would like to know how many province and how many cities in Thailand.
Country Name | No. Provinces | No. Cities
Thailand | 77 | 1234
I guess that it need to use multiple COUNT(*) but I dont know how to use it.
Anybody know please suggest solution?
You need to use GROUP BY and COUNT:
SELECT c.name, count(distinct p.id) provincecoutn, count(distinct city.id) citycount
FROM country c
LEFT JOIN province p on c.id = p.country_id
LEFT JOIN City on p.id = city.province_id
GROUP BY c.name
Good luck.
Try this:
SELECT
C.Name, COUNT(DISTINCT P.Id) NoProvance, COUNT(CC.Id) NoCities
FROM country C
JOIN province P
ON C.Id = P.COUNTRY_ID
JOIN city CC
ON P.Id = CC.province_id
WHERE C.Name = 'Thailand'
GROUP BY C.Name
SQL FIDDLE DEMO
It's probably faster to count cities per province before joining to province:
SELECT c.name AS "Country Name"
,count(p.id) AS "No. Provinces"
,sum(ci.city_ct) AS "No. Cities"
FROM country c
LEFT JOIN province p ON p.country_id = c.id
LEFT JOIN (
SELECT province_id, count(*) AS city_ct FROM city GROUP BY 1
) ci ON ci.province_id = p.id
GROUP BY 1
-> sqlfiddle for PostgreSQL(!)

Selecting most recent record from multiple tables

Newbie to SQL, just stepped out of my comfort zone. I'm using MySQL in a WPF application.
I have three tables in my database.
Patients:
ID | Name | ...
Referrals:
ID | FK_Patient_ID | ...
Visits:
ID | FK_Referral_ID | Date | FollowUpDate | FollowUpInterval | ...
The 'FK' fields are foreign keys into the other tables. So a visit belongs to a referral, and a referral belongs to a patient.
I want the get the most recent visit for each patient (or referral, since you can't have a visit without a referral) and get the following:
patients.ID | patients.Name | visits.FollowUpDate | visits.FollowUpInterval
What I'm trying to do is get a list of patients who have missed their follow up visits.
Hopefully this is a no brainer for you SQL people out there...
SELECT p1.ID
,p1.Name
,v1.FollowUpDate
,v1.FollowUpInterval
FROM Patients p1
INNER JOIN
Referals r1 ON p1.ID=r1.FK_Patient_ID
INNER JOIN
Visits v1 ON r1.ID=v1.FK_Referral_ID
INNER JOIN (
SELECT MAX(v.ID) AS ID
FROM Patients p
INNER JOIN
Referals r ON p.ID=r.FK_Patient_ID
INNER JOIN
Visits v ON r.ID=v.FK_Referral_ID
GROUP BY p.ID) v2 ON v1.ID=v2.ID
I'm not 100% sure if this works with MySQL, but here's one way that you could do it in SQL Server, and I think it's portable:
SELECT p.ID, p.NAME, v.FollowUpDate, v.FollowUpInterval
FROM Patients p
JOIN Referrals r ON p.ID = r.FK_PatientID
JOIN Visits v ON ON r.ID = v.FK_Referral_ID
JOIN (SELECT r.FK_Patient_ID, MAX(v.Date) AS [Date]
FROM Referrals r
JOIN Visits v ON r.ID = v.FK_Referral_ID
GROUP BY r.FK_Patient_ID
) x ON p.ID = x.FK_Patient_ID
AND v.Date = x.Date
Basically, you use a subquery to find the most recent visit by patient, and then join it back to your original tables to pull back the rows that match that value. This only works if there was one row with that date.
I used the script as below on PostgeSQL and it worked!
SELECT p.ID, p.NAME, v.FollowUpDate, v.FollowUpInterval
FROM Patients p
INNER JOIN Referrals r ON p.ID = r.FK_PatientID
INNER JOIN Visits v ON ON r.ID = v.FK_Referral_ID
INNER JOIN (SELECT r.FK_Patient_ID, MAX(v.Date) AS "Date"
FROM Referrals r
INNER JOIN Visits v ON r.ID = v.FK_Referral_ID
GROUP BY
r.FK_Patient_ID
) x ON p.ID = x.FK_Patient_ID AND v.Date = x.Date