Joining two queries together where one has a subquery - sql

Hello I want to join these two queries together. I have tried many combinations of writing and rewriting the code and I have become stuck.
The two queries I want to join are:
SELECT a.lastname, a.firstname
FROM athletes a,
(SELECT athlete#, COUNT(event#)
FROM results
GROUP BY athlete#
HAVING COUNT(event#) > 1 ) b
WHERE a.athlete# = b.athlete#
ORDER BY a.lastname;
and
SELECT sc.datetime, e.name
FROM event e JOIN schedule sc USING (event#);
The first query does a count on the athletes having more than one event and returns the correct number of 19, when I try to add the 2nd query into the first I end up pulling most of the data from the results table.

If you want events for athletes have competed more than once, which seems like a reasonable interpretation, then:
select a.lastname, a.firstname, e.name, sc.datetime
from athletes a join
(select r.*, count(*) over (partition by athelete#) as cnt
from results r
) r
on a.athlete# = r.athelete# and cnt > 1 join
event e
on r.event# = e.event# join
schedule s
on e.event# = s.event#;

SELECT a.lastname, a.firstname, sc.datetime, e.name
FROM athletes a
inner join (SELECT athlete#, COUNT(event#)
FROM results
GROUP BY athlete#
HAVING COUNT(event#) > 1 ) b on a.athlete# = b.athlete#
inner join results r on r.athlete# =b.athlete#
inner join event e on e.event# = r.event#
inner join schedule sc on sc.event# = e.event#
ORDER BY a.lastname;

Related

Filter results through two values in the same column if one is multiple

I am trying to list customers who have purchased from ProductCategoryKey 1, and if those customers have purchased ProductCategoryKEY 3 more than once show only one row for that customer. My code so far only filters through PC Key 1. How can I say 'of those who have purchased PC Key 1, how many have purchased PC Key 3 at least two times and only show that person once?'
SELECT DISTINCT C.FirstName, C.LastName, PC.EnglishProductCategoryName
FROM dbo.FactInternetSales AS I
INNER JOIN dbo.DimCustomer AS C
ON I.CustomerKey = C.CustomerKey
INNER JOIN dbo.DimProduct AS P
ON I.ProductKey = P.ProductKey
INNER JOIN dbo.DimProductSubcategory AS SC
ON P.ProductSubcategoryKey = SC.ProductSubcategoryKey
INNER JOIN dbo.DimProductCategory as PC
ON SC.ProductCategoryKey = PC.ProductCategoryKey
WHERE PC.ProductCategoryKey = 1
ORDER BY C.LastName ASC, FirstName ASC;
Using Microsoft SQL Server 2019
ADventureWorksDW2017
I'm a big fan of using temp tables, or variable tables, as a way to further filter data. The first query gets all customers with a PC Key of 1 and 3, and inserts those records into a temp table. Then, the second query uses aggregation and a GROUP BY clause to count all rows where the PC Key is 3, followed by a HAVING clause where the count is > 2. The GROUP BY clause in the second query effectively creates a distinct record-set for your last requirement.
I break up the query into two queries for readability. You could also use nested sub queries to achieve the same result.
SELECT DISTINCT C.FirstName, C.LastName, PC.EnglishProductCategoryName,
PC.ProductCategoryKey
into #temp1
FROM dbo.FactInternetSales AS I
INNER JOIN dbo.DimCustomer AS C
ON I.CustomerKey = C.CustomerKey
INNER JOIN dbo.DimProduct AS P
ON I.ProductKey = P.ProductKey
INNER JOIN dbo.DimProductSubcategory AS SC
ON P.ProductSubcategoryKey = SC.ProductSubcategoryKey
INNER JOIN dbo.DimProductCategory as PC
ON SC.ProductCategoryKey = PC.ProductCategoryKey
WHERE PC.ProductCategoryKey = 1
AND PC.ProductCategoryKey = 3
ORDER BY C.LastName ASC, FirstName ASC
SELECT FirstName, LastName, EnglishProductCategoryName, COUNT(*)
FROM #temp1
WHERE ProductCategoryKey = 3
GROUP BY FirstName, LastName, EnglishProductCategoryName
HAVING COUNT(*) > 2
I would use a CTE over a temp table unless you have a performance issue. This way you get a single execution plan which is often better (except when its not) than splitting into two execution plans (as using a temp table does).
Also you shouldn't need a distinct unless you have your logic wrong. A correctly grouped query very rarely needs a distinct.
WITH cte AS (
SELECT C.FirstName, C.LastName, PC.EnglishProductCategoryName, PC.ProductCategoryKey
FROM dbo.FactInternetSales AS I
INNER JOIN dbo.DimCustomer AS C ON I.CustomerKey = C.CustomerKey
INNER JOIN dbo.DimProduct AS P ON I.ProductKey = P.ProductKey
INNER JOIN dbo.DimProductSubcategory AS SC ON P.ProductSubcategoryKey = SC.ProductSubcategoryKey
INNER JOIN dbo.DimProductCategory AS PC ON SC.ProductCategoryKey = PC.ProductCategoryKey
WHERE PC.ProductCategoryKey = 1 AND PC.ProductCategoryKey = 3
)
SELECT FirstName, LastName, EnglishProductCategoryName, COUNT(*)
FROM cte
WHERE ProductCategoryKey = 3
GROUP BY FirstName, LastName, EnglishProductCategoryName
HAVING COUNT(*) > 2
ORDER BY LastName ASC, FirstName ASC;
I am assuming the logic is correct, because without sample data and expected results its hard to know.

SQL Server Circular Query

I have 4 tables, in that I want to fetch records from all 4 and aggregate the values
I have these tables
I am expecting this output
but getting this output as a Cartesian product
It is multiplying the expenses and allocation
Here is my query
select
a.NAME, b.P_NAME,
sum(a.DURATION) DURATION,
sum(b.[EXP]) EXPEN
from
(select
e.ID, a.P_ID, e.NAME, a.DURATION DURATION
from
EMPLOYEE e
inner join
ALLOCATION a ON e.ID = a.E_ID) a
inner join
(select
p.P_ID, e.E_ID, p.P_NAME, e.amt [EXP]
from
PROJECT p
inner join
EXPENSES e ON p.P_ID = e.P_ID) b ON a.ID = b.E_ID
and a.P_ID = b.P_ID
group by
a.NAME, b.P_NAME
Can anyone suggest something about this.
The following should work:
SELECT e.Name,p.Name,COALESCE(d.Duration,0),COALESCE(exp.Expen,0)
FROM
Employee e
CROSS JOIN
Project p
LEFT JOIN
(SELECT E_ID,P_ID,SUM(Duration) as Duration FROM Allocation
GROUP BY E_ID,P_ID) d
ON
e.E_ID = d.E_ID and
p.P_ID = d.P_ID
LEFT JOIN
(SELECT E_ID,P_ID,SUM(AMT) as Expen FROM Expenses
GROUP BY E_ID,P_ID) exp
ON
e.E_ID = exp.E_ID and
p.P_ID = exp.P_ID
WHERE
d.E_ID is not null or
exp.E_ID is not null
I've tried to write a query that will produce results where e.g. there are rows in Expenses but no rows in Allocations (or vice versa) for some particular E_ID,P_ID combination.
Use left join in select query by passing common id for all table
Hi I got the answer what I want from some modification in the query
The above query is also working like a charm and have done some modification to the original query and got the answer
Just have to group by the inner queries and then join the queries it will then not showing Cartesian product
Here is the updated one
select a.NAME,b.P_NAME,sum(a.DURATION) DURATION,sum(b.[EXP]) EXPEN from
(select e.ID,a.P_ID, e.NAME,sum(a.DURATION) DURATION from EMPLOYEE e inner join ALLOCATION a
ON e.ID=a.E_ID group by e.ID,e.NAME,a.P_ID) a
inner join
(select p.P_ID,e.E_ID, p.P_NAME,sum(e.amt) [EXP] from PROJECT p inner join EXPENSES e
ON p.P_ID=e.P_ID group by p.P_ID,p.P_NAME,e.E_ID) b
ON a.ID=b.e_ID and a.P_ID=b.P_ID group by a.NAME,b.P_NAME
Showing the correct output

How to write a Sql query to finds all participating rows in another table?

There are 3 tables: events, eventClients and clients.
Write a query that finds events that all clients have access to?
Inner join doesn't guarantee that all rows in a table participate so this doesn't help:
select * from events e
inner join eventclients ec on e.id = ec.eventid
inner join clients c on etc.clientid = c.id
You can use a nested NOT EXISTS to check this:
SELECT e.*
FROM events e
WHERE NOT EXISTS(
SELECT 1 FROM client c
WHERE NOT EXISTS(
SELECT 1 FROM eventclients ec
WHERE ec.eventid = e.id AND c.id= ec.clientid
)
)
Demo
You can also do
SELECT e.id, e.name
FROM events e LEFT JOIN eventclients ec
ON e.id = ec.eventid LEFT JOIN clients c
ON ec.clientid = c.id
GROUP BY e.id, e.name
HAVING COUNT(ec.clientid) =
(
SELECT COUNT(*) FROM clients
)
Here is SQLFiddle demo
Assuming that you mean event has a many-to-many relationship with client and event_client is the bridge table between them, then you should any of the many other questions asking about a SQL join across a many-to-many relationship.
SQL many to many select
How to filter SQL results in a has-many-through relation

Using an inner select construction for a select value

I would like to hear if anyone can tell me a simple syntax that accomplishes the same as the following (with the same flexibility):
SELECT C.CompanyName,
(SELECT Count(*) FROM Employees WHERE CompanyId = C.Id) as EmployeeCount
FROM Company C
Now, what's important is that the inner SELECT giving the EmployeeCount is:
An independent SELECT statement
This means that it should work with any existing SELECT, even if it already contains joins etc.
Can use values from the parent SELECT
I know that this scenario can be easily accomplished in other ways, but the above is a simplified example to explain the challenge. My real scenario is a complex SELECT statement where I do not want to complicate it by adding more joins. Performance is no issue.
Using INNER JOIN:
SELECT C.CompanyName, Count(E.*) as EmployeeCount
FROM Company C
INNER JOIN Employees E on E.CompanyId = C.Id
Using NESTED JOIN:
SELECT C.CompanyName, Count(E.1) as EmployeeCount
FROM Company C, Employess E
WHERE E.CompanyId = C.Id
If you want to use the same syntax, at least put this:
SELECT C.CompanyName,
(SELECT Count(1) FROM Employees WHERE CompanyId = C.Id) as EmployeeCount
FROM Company C
If you need all the data to be shown, even the ones the companies without any Employees, you can use a LEFT OUTER JOIN:
SELECT C.CompanyName, Count(E.*) as EmployeeCount
FROM Company C
LEFT OUTER JOIN Employees E on E.CompanyId = C.Id
Try using a derived table, which statifies both your conditions.
An independent SELECT statement.
a. Using a Derived Table allows you to keep your independent Select Statement
Can use values from the parent SELECT.
a. As an Inner join you can still use values from the parent select.
SELECT
C.CompanyName,
EC.EmployeeCount
FROM Company C
INNER JOIN (SELECT
Count(*) AS EmployeeCount
FROM Employees ) EC
ON WHERE EC.CompanyId = C.Id
If your inner select is complicated, then why not make a view of it:
CREATE VIEW EmpSelect AS
SELECT CompanyId, whatever FROM Employees;
Then
SELECT
C.CompanyName, Count(*) AS EmpCount
FROM
Company C
LEFT JOIN EmpSelect E
ON C.Id = E.CompanyId
GROUP BY
C.CompanyName;

SQL Return only where more than one join

Not sure how to ask this as I'm a bit of a database noob,
What I want to do is the following.
table tb_Company
table tb_Division
I want to return companies that have more than one division and I don't know how to do the where clause.
SELECT dbo.tb_Company.CompanyID, dbo.tb_Company.CompanyName,
dbo.tb_Division.DivisionName FROM dbo.tb_Company INNER JOIN dbo.tb_Division ON
dbo.tb_Company.CompanyID = dbo.tb_Division.DivisionCompanyID
Any help or links much appreciated.
You'll need another JOIN where you only return companies having more than one division by using a GROUP BYand a HAVINGclause.
You can read up on grouping here
Groups a selected set of rows into a
set of summary rows by the values of
one or morecolumns or expressions. One
row is returned for each group.
Aggregate functions in the SELECT
clause list provide
information about each group instead
of individual rows.
SELECT dbo.tb_Company.CompanyID
, dbo.tb_Company.CompanyName
, dbo.tb_Division.DivisionName
FROM dbo.tb_Company
INNER JOIN dbo.tb_Division ON dbo.tb_Company.CompanyID = dbo.tb_Division.DivisionCompanyID
INNER JOIN (
SELECT DivisionCompanyID
FROM dbo.tb_Division
GROUP BY
DivisionCompanyID
HAVING COUNT(*) > 1
) d ON d.DivisionCompanyID = dbo.tb_Company.CompanyID
another alternative...
SELECT c.CompanyId, c.CompanyName, d.DivisionName
FROM tbl_Company c
INNER JOIN tbl_Division d ON c.CompanyId=d.DivisionCompanyId
GROUP BY c.CompanyId, c.CompanyName, d.DivisionName
HAVING COUNT(*) > 1
How about?
WITH COUNTED AS
(
SELECT C.CompanyID, C.CompanyName, D.DivisionName,
COUNT() OVER(PARTITION BY C.CompanyID) AS Cnt
FROM dbo.tb_Company C
INNER JOIN dbo.tb_Division D ON C.CompanyID = D.DivisionCompanyID
)
SELECT *
FROM COUNTED
WHERE Cnt > 1
With the other solutions (that join onto Division table twice), a single company/division can be returned under a heavy insert load.
If a row is inserted into the Division table between the time the first join occurs and the time the second join (with the group by/having) is evaluated, the first Division join will return a single row. However, the second one will return a count of 2.
How about...
SELECT dbo.tb_Company.CompanyID,
dbo.tb_Company.CompanyName,
FROM dbo.tb_Company
WHERE (SELECT COUNT(*)
FROM dbo.tb_Division
WHERE dbo.tb_Company.CompanyID =
dbo.tb_Division.DivisionCompanyID) > 1;