sql count of multiple tables possible inner join - sql

I have a Table called Jobs and 02 other tables (places and times) that connect to Jobs by the JobID.
So, based on the tables below, I need the following in SQL server:
Jobs (JobID,Company)
123 ABC
456 DEF
789 GHI
PLACES (Country,JobID)
BR 123
EU 123
CA 456
TIMES(time,JobID)
05 456
08 123
09 789
Needed query Result:
QRYRESULT(JobID,CountOFPLaces,CountofTimes)
123 , 2, 1
456 , 1 , 1
789 , , 1
Thanks for the help!

There keys here are knowing that using an outer join can give you all results for all jobs and that a count(distinct field) will give you distinct counts eliminating the additional counts added by joins (assuming each record is unique and that duplicates don't exist in a given table)
SELECT J.jobID,
count(Distinct P.Country) as CountOfPlaces,
Count(Distinct T.Time) as CountOfTimes
FROM Jobs J
LEFT JOIN Places P
on J.JobID=P.JobID
LEFT JOIN Times T
on T.JobID = J.JobID
GROUP BY J.JobID

select id, pc, pt
from jobs j
left join (select jobid, count(Country) pc from Places group by JobId) p
on j.id = p.JobId
left join (select jobid, count([time]) pt from Times group by JobId) t
on j.id = t.JobId
You should use "group by" statement to count rows with the same job id for the tables "Places" and "Times" and then join it to "Jobs" table.
I also wanted to focus that you should use subquery in join statement, otherwise you get wrong data.

Related

Access SQL JOIN with subquery

Can someone tell me how I can convert this query to work in MS Access?
SELECT Shooters.idShooters, Shooters.firstname, Shooters.lastname, JoinedContingent.Count, JoinedShots.Count
FROM Shooters
INNER JOIN
(SELECT Shooters.idShooters, Count(Contingent.idContingent) AS Count
FROM Shooters LEFT JOIN Contingent ON Shooters.idShooters = Contingent.fidShooter
GROUP BY Shooters.idShooters)
AS JoinedContingent ON JoinedContingent.idShooters = Shooters.idShooters
INNER JOIN
(SELECT Shooters.idShooters, Count(Shots.idShots) AS Count
FROM Shooters LEFT JOIN Shots ON Shooters.idShooters = Shots.fidShooters
GROUP BY Shooters.idShooters)
AS JoinedShots ON JoinedShots.idShooters = Shooters.idShooters;
Background information:
I would like to count the foreign key occurrences for each Shooter in the table Contingent and Shots. The result should look like this:
idShooters | firstname | lastname | Count | Count
____________________________________________________________
1 John Doe 0 10
2 Jane Doe 1 20
.
.
.
I think this is what you want:
SELECT s.idShooters, s.firstname, s.lastname,
NZ(c.Count, 0), NZ(sh.Count, 0)
FROM (Shooters as s LEFT JOIN
(SELECT c.fidShooter, Count(*) AS Count
FROM Contingent as c
GROUP BY c.fidShooter
) as c
ON s.idShooters = c.fidShooter
) LEFT JOIN
(SELECT sh.fidShooters, Count(*) AS Count
FROM Shots as sh
GROUP BY sh.fidShooters
) as sh
ON s.idShooters = sh.fidShooters;
Note that I moved the outer join to the outer query. Actually, no joins are needed in the subqueries, so don't bother.

Most popular pairs of shops for workers from each company

I've got 2 tables, one with sales and one with companies:
Sales Table
Transaction_Id Shop_id Sale_date Client_ID
92356 24234 11.09.2018 12356
92345 32121 11.09.2018 32121
94323 24321 11.09.2018 21231
94278 45321 11.09.2018 42123
Company table
Client_ID Company_name
12345 ABC
13322 ABC
32321 BCD
22221 BCD
What I want to achieve is distinct count of Clients from each Company for each pair of shops(Clients who had at least 1 transaction in both of shops) :
Shop_Id_1 Shop_id_2 Company_name Count(distinct Client_id)
12356 12345 ABC 31
12345 14278 ABC 23
14323 12345 BCD 32
14278 12345 BCD 43
I think that I have to use self join, but my queries even with filter for one week is killing DB, any thoughts on that? I'm using Microsoft SQL server 2012.
Thanks
I think this is a self-join and aggregation, with a twist. The twist is that you want to include the company in each sales record, so it can be used in the self-join:
with sc as (
select s.*, c.company_name
from sales s join
companies c
on s.client_id = c.client_id
)
select sc1.shop_id, sc2.shop_id, sc1.company_name, count(distinct sc1.client_id)
from sc sc1 join
sc sc2
on sc1.client_id = sc2.client_id and
sc1.company_name = sc2.company_name
group by sc1.shop_id, sc2.shop_id, sc1.company_name;
I think there are some issues with your question. I interpreted it as such that the company table contains the shop ID's, not the ClienId's.
First you can create a solution to get the shops as rows for each company. Here I chose a maximum of 5 shops per company. Don't forget the semicolon in the previous statement before the cte's.
WITH CTE_Comp AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY CompanyName ORDER BY ShopID) AS RowNumb
FROM Company AS C
)
SELECT C1.ShopID,
C2.ShopID AS ShopID_2,
C3.ShopID AS ShopID_3,
C4.ShopID AS ShopID_4,
C5.ShopID AS ShopID_5,
C1.CompanyName
INTO ShopsByCompany
FROM CTE_Comp AS C1
LEFT JOIN CTE_Comp AS C2 ON C1.CompanyName= C2.CompanyName AND RowNumb = 2
LEFT JOIN CTE_Comp AS C2 ON C1.CompanyName= C3.CompanyName AND RowNumb = 3
LEFT JOIN CTE_Comp AS C2 ON C1.CompanyName= C4.CompanyName AND RowNumb = 4
LEFT JOIN CTE_Comp AS C2 ON C1.CompanyName= C5.CompanyName AND RowNumb = 5
WHERE C1.RowNumb = 1
After that, in a few steps, I think you could get the desired result:
WITH ClientsPerShop AS
(
SELECT ShopID,
COUNT (DISTINCT ClientID) AS TotalClients
FROM Sales
GROUP BY ShopID
)
, ClienstsPerCompany AS
(
SELECT CompanyName,
SUM (TotalClients) AS ClientsPerComp
FROM Company AS C
INNER JOIN ClientsPerShop AS CPS ON C.ShopID = CPS.ShopID
GROUP BY CompanyName
)
SELECT *
FROM ClienstsPerCompany AS CPA
INNER JOIN ShopsByCompany AS SBC ON SBC.CompanyName = CPA.CompanyName
Hopefully this will bring you closer to your solution, best of luck!

Grouping the data and showing 1 row per group in postgres

I have two tables which look like this :-
Component Table
Revision Table
I want to get the name,model_id,rev_id from this table such that the result set has the data like shown below :-
name model_id rev_id created_at
ABC 1234 2 23456
ABC 5678 2 10001
XYZ 4567
Here the data is grouped by name,model_id and only 1 data for each group is shown which has the highest value of created_at.
I am using the below query but it is giving me incorrect result.
SELECT cm.name,cm.model_id,r.created_at from dummy.component cm
left join dummy.revision r on cm.model_id=r.model_id
group by cm.name,cm.model_id,r.created_at
ORDER BY cm.name asc,
r.created_at DESC;
Result :-
Anyone's help will be highly appreciated.
use max and sub-query
select T1.name,T1.model_id,r.rev_id,T1.created_at from
(
select cm.name,
cm.model_id,
MAX(r.created_at) As created_at from dummy.component cm
left join dummy.revision r on cm.model_id=r.model_id
group by cm.name,cm.model_id
) T1
left join revision r
on T1.created_at =r.created_at
http://www.sqlfiddle.com/#!17/68cb5/4
name model_id rev_id created_at
ABC 1234 2 23456
ABC 5678 2 10001
xyz 4567
In your SELECT you're missing rev_id
Try this:
SELECT
cm.name,
cm.model_id,
MAX(r.rev_id) AS rev_id,
MAX(r.created_at) As created_at
from dummy.component cm
left join dummy.revision r on cm.model_id=r.model_id
group by 1,2
ORDER BY cm.name asc,
r.created_at DESC;
What you were missing is the statement to say you only want the max record from the join table. So you need to join records, but the join will bring in all records from table r. If you group by the 2 columns in component, then select the max from r, on the id and created date, it'll only pick the top out the available to join
I would use distinct on:
select distinct on (m.id) m.id, m.name, r.rev_id, r.created_at
from model m left join
revision r
on m.model_id = r.model_id
order by m.id, r.rev_id;

Oracle SQL: SQL join with group by (count) and having clauses

I have 3 tables
Table 1.) Sale
Table 2.) ItemsSale
Table 3.) Items
Table 1 and 2 have ID in common and table 2 and 3 have ITEMS in common.
I'm having trouble with a query that I have made so far but can't seem to get it right.
I'm trying to select all the rows that only have one row and match a certain criteria here is my query:
select *
from sales i
inner join itemssales j on i.id = j.id
inner join item u on j.item = u.item
where u.code = ANY ('TEST','HI') and
i.created_date between TO_DATE('1/4/2016 12:00:00 AM','MM/DD/YYYY HH:MI:SS AM') and
TO_DATE('1/4/2016 11:59:59 PM','MM/DD/YYYY HH:MI:SS PM')
group by i.id
having count(i.id) = 1
In the ItemSale table there are two entries but in the sale table there is only one. This is fine...but I need to construct a query that will only return to me the one record.
I believe the issue is with the "ANY" portion, the query only returns one row and that row is the record that doesn't meet the "ANY ('TEST', 'HI')" criteria.
But in reality that record with that particular ID has two records in ItemSales.
I need to only return the records that legitimately only have one record.
Any help is appreciated.
--EDIT:
COL1 | ID
-----|-----
2 | 26
3 | 85
1 | 23
1 | 88
1 | 6
1 | 85
What I also do is group them and make sure the count is equal to 1 but as you can see, the ID 85 is appearing here as one record which is a false positive because there is actually two records in the itemsales table.
I even tried changing my query to j.id after the select since j is the table with the two records but no go.
--- EDIT
Sale table contains:
ID
---
85
Itemsales table contains:
ID | Position | item_id
---|----------|---------
85 | 1 | 6
85 | 2 | 7
Items table contains:
item_id | code
--------|------
7 | HI
6 | BOOP
The record it is returning is the one with the Code of 'BOOP'
Thanks,
"I need to only return the records that legitimately only have one record."
I interpret this to mean, you only want to return SALES with only one ITEM. Furthermore you need that ITEM to meet your additional criteria.
Here's one approach, which will work fine with small(-ish) amounts of data but may not scale well. Without proper table descriptions and data profiles it's not possible to offer a performative solution.
with itmsal as
( select sales.id
from itemsales
join sales on sales.id = itemsales.id
where sales.created_date >= date '2016-01-04'
and sales.created_date < date '2016-01-05'
group by sales.id having count(*) = 1)
select sales.*
, item.*
from itmsal
join sales on sales.id = itmsal.id
join itemsales on itemsales.id = itmsal.id
join items on itemsales.item = itemsales.item
where items.code in ('TEST','HI')
I think you are trying to restrict the results so that items MUST ONLY have the code of 'TEST' or 'HI'.
select
sales.*
from (
select
s.id
from Sales s
inner join Itemsales itss on s.id = itss.id
inner join Items i on itss.item_id = i.item_id
group by
s.id
where s.created_date >= date '2016-01-04'
and s.created_date < date '2016-01-05'
having
sum(case when i.code IN('TEST','HI') then 0 else 1 end) = 0
) x
inner join sales on x.id = sales.id
... /* more here as required */
This construct only returns sales.id that have items with ONLY those 2 codes.
Note it could be done with a common table expression (CTE) but I prefer to only use those when there is an advantage in doing so - which I do not see here.
If I get it correctly this may work (not tested):
select *
from sales s
inner join (
select i.id, count( i.id ) as cnt
from sales i
inner join itemssales j on i.id = j.id
inner join item u on j.item = u.item and u.code IN ('TEST','HI')
where i.created_date between TO_DATE('1/4/2016 12:00:00 AM','MM/DD/YYYY HH:MI:SS AM') and
TO_DATE('1/4/2016 11:59:59 PM','MM/DD/YYYY HH:MI:SS PM')
group by i.id
) sj on s.id = sj.id and sj.cnt = 1

Query Returning SUM of Quantity or 0 if no Quantity for a Product at a given date

I have the following 4 tables:
----------
tblDates:
DateID
30/04/2012
01/05/2012
02/05/2012
03/05/2012
----------
tblGroups:
GroupID
Home
Table
----------
tblProducts:
ProductID GroupID
Chair Home
Fork Table
Knife Table
Sofa Home
----------
tblInventory:
DateID ProductID Quantity
01/05/2012 Chair 2
01/05/2012 Sofa 1
01/05/2012 Fork 10
01/05/2012 Knife 10
02/05/2012 Sofa 1
02/05/2012 Chair 3
03/05/2012 Sofa 2
03/05/2012 Chair 3
I am trying to write a query that returns all Dates in tblDates, all GroupIDs in tblGroups and the total Quantity of items in each GroupID.
I manage to do this but only get Sum(Quantity) for GroupID and DateID that are not null. I would like to get 0 instead. For exemple for the data above, I would like to get a line "02/05/2012 Table 0" as there is no data for any product in "Table" group on 01/05/12.
The SQL query I have so far is:
SELECT tblDates.DateID,
tblProducts.GroupID,
Sum(tblInventory.Quantity) AS SumOfQuantity
FROM (tblGroup
INNER JOIN tblProducts ON tblGroup.GroupID = tblProducts.GroupID)
INNER JOIN (tblDates INNER JOIN tblInventory ON tblDates.DateID = tblInventory.DateID) ON tblProducts.ProductID = tblInventory.ProductID
GROUP BY tblDates.DateID, tblProducts.GroupID;
I reckon I should basically have the same Query on a table different from tblInventory that would list all Products instead of listed products with 0 instead of no line but I am hesitant to do so as given the number of Dates and Products in my database, the query might be too slow.
There is probably a better way to achieve this.
Thanks
To get all possible Groups and Dates combinations, you'll have to CROSS JOIN those tables. Then you can LEFT JOIN to the other 2 tables:
SELECT
g.GroupID
, d.DateID
, COALESCE(SUM(i.Quantity), 0) AS Quantity
FROM
tblDates AS d
CROSS JOIN
tblGroups AS g
LEFT JOIN
tblProducts AS p
JOIN
tblInventory AS i
ON i.ProductID = p.ProductID
ON p.GroupID = g.GroupID
AND i.DateID = d.DateID
GROUP BY
g.GroupID
, d.DateID
use the isnull() function. change your query to:
SELECT tblDates.DateID, tblProducts.GroupID,
**ISNULL( Sum(tblInventory.Quantity),0)** AS SumOfQuantity
FROM (tblGroup INNER JOIN tblProducts ON tblGroup.GroupID = tblProducts.GroupID)
INNER JOIN (tblDates INNER JOIN tblInventory ON tblDates.DateID = tblInventory.DateID)
ON tblProducts.ProductID = tblInventory.ProductID
GROUP BY tblDates.DateID, tblProducts.GroupID;
You don't say which database you're using but what I'm familiar with is Oracle.
Obviously, if you inner join all tbales, you will not get the rows containing nulls. If you use an outer join to the inventory table you also have a problem because the groupid column is not listed in tblinventory and you can only outer join to one table.
I'd say you have two choices. Use a function or have a duplicate of the groupid column in the inventory table.
So, a nicer but slower solution:
create or replace
function totalquantity( d date, g varchar2 ) return number as
result number;
begin
select nvl(sum(quantity), 0 )
into result
from tblinventory i, tblproducts p
where i.productid = p.productid
and i.dateid = d
and p.groupid = g;
return result;
end;
select dateid, groupid, totalquantity( dateid, groupid )
from tbldates, tblgroups
Or, if you include the groupid column in the inventory table. (You can still quarantee integrity using constraints)
select d.dateid, i.groupid, nvl(i.quantity, 0)
from tbldates d, tblinventory i
where i.dateid(+) = d.dateid
group by d.dateid, g.groupid
Hope this helps,
GĂ­sli