How to Fix Right Outer Join - sql

I am Trying to make Left Outer Join Use bellow code;
The Result Using (LEFT OUTER , RIGHT OUTER, FULL OUTER And INNER JOIN) is The same Result.
I am Try to return All Publisher Name Include Those who don't connected with any Books yet!
SELECT P.PublisherName,
COUNT(B.BookID) AS BookPublished
FROM LR_Publisher AS P LEFT OUTER JOIN LR_Book As B
ON P.PublisherID = B.PublisherID
WHERE (P.PublisherID = #pPublisherID OR #pPublisherID IS NULL)
GROUP BY PublisherName
Thanks In Advance

Publishers that have no books published are filtered out by GROUP BY.
It can be fixed by using GROUP BY ALL:
SELECT P.PublisherName,
COUNT(B.BookID) AS BookPublished
FROM LR_Publisher AS P
LEFT OUTER JOIN LR_Book As B ON P.PublisherID = B.PublisherID
-- WHERE (P.PublisherID = #pPublisherID OR #pPublisherID IS NULL)
GROUP BY PublisherName

you could write it this way
select P.PublisherName,
isnull(bi.BookPublished, 0) as BookPublished
from LR_Publisher as P
left join (
select B.PublisherID, Count(B.BookID) BookPublished
from LR_Book as B
where (#pPublisherID is null or B.PublisherID = #pPublisherID)
group by B.PublisherID
) bi on P.PublisherID = Bi.PublisherID
where (#pPublisherID is null or P.PublisherID = #pPublisherID)
I also was not aware of the filtering done by group by. Thank you for the question.

simple left join will help you want all publisher name including who does not published any book yet
SELECT P.PublisherName
FROM LR_Publisher AS P LEFT OUTER JOIN LR_Book As B
ON P.PublisherID = B.PublisherID

Related

SQL many to many select people with multiple vacancies

I am working with sql server through SSMS right now. How can i choose all people with multiple(>2)vacancies?
I am trying something like that, but i dont understand how to make part with "more than 2 vacancies"?
SELECT dbo.applicants.FirstName, dbo.vacancy.Name
FROM dbo.applicants INNER JOIN
dbo.VacancyApplicant ON dbo.applicants.id = dbo.VacancyApplicant.ApplicantId INNER JOIN
dbo.vacancy ON dbo.VacancyApplicant.VacancyId = dbo.vacancy.id WHERE dbo.vacancy.Name='third vacancy'
SELECT dbo.applicants.FirstName, dbo.vacancy.Name
FROM dbo.applicants A INNER JOIN
dbo.VacancyApplicant V ON A.id = V.ApplicantId
WHERE EXIST(
SELECT 1
FROM dbo.applicants INNER JOIN
dbo.VacancyApplicant ON dbo.applicants.id =
dbo.VacancyApplicant.ApplicantId INNER JOIN
dbo.vacancy ON dbo.VacancyApplicant.VacancyId = dbo.vacancy.id
WHERE A.id=dbo.applicants.id
GROUP BY dbo.applicants.id,dbo.vacancy.id
HAVING COUNT(1)>2
)
Group By and Having are you basic answer. Below is a simple solution, might not be ideal, but can give you the idea.
I am finding target "applicants" ids in subquery, that uses GROUP BY and HAVING then outer query joins to that to output FirstName and LastName of applicant
SELECT dbo.applicants.FirstName, dbo.applicants.LastName FROM
dbo.applicants a INNER JOIN
(
SELECT dbo.applicants.id
FROM dbo.applicants INNER JOIN
dbo.VacancyApplicant ON dbo.applicants.id = dbo.VacancyApplicant.ApplicantId INNER JOIN
dbo.vacancy ON dbo.VacancyApplicant.VacancyId = dbo.vacancy.id AND dbo.vacancy.Name='third vacancy'
GROUP BY dbo.applications.id
HAVING COUNT(dbo.vacancy.id) > 2
) targetIds ON a.id = targetIds.id
"more than 2 vacancies"?
Your question only mentions vacancies but your query is filtering for a particular name. I assume you really want more than two of that name.
If I understand correctly, you want aggregation:
SELECT a.FirstName, a.Name
FROM dbo.applicants a INNER JOIN
dbo.VacancyApplicant va
ON a.id = va.ApplicantId INNER JOIN
dbo.vacancy v
ON va.VacancyId = v.id
WHERE v.Name = 'third vacancy'
GROUP BY a.FirstName, v.Name
HAVING COUNT(*) > 2;
Note the use of table aliases. They make the query easier to write and to read.
WITH TempCTE AS (
SELECT DISTINCT ap.FirstName
,vc.Name
,COUNT (va.VacancyId) OVER (PARTITION BY ap.id) AS NoOfVacancies
FROM dbo.applicants ap
JOIN dbo.VacancyApplicant va
ON ap.id = va.ApplicantId
JOIN dbo.vacancy vc
ON va.VacancyId = vc.id
)
SELECT FirstName,[Name], NoOfVacancies FROM TempCTE
WHERE NoOfVacancies > 2

How to create distinct count from queries with several tables

I am trying to create one single query that will give me a distinct count for both the ActivityID and the CommentID. My query in MS Access looks like this:
SELECT
tbl_Category.Category, Count(tbl_Activity.ActivityID) AS CountOfActivityID,
Count(tbl_Comments.CommentID) AS CountOfCommentID
FROM tbl_Category LEFT JOIN
(tbl_Activity LEFT JOIN tbl_Comments ON
tbl_Activity.ActivityID = tbl_Comments.ActivityID) ON
tbl_Category.CategoryID = tbl_Activity.CategoryID
WHERE
(((tbl_Activity.UnitID)=5) AND ((tbl_Comments.PeriodID)=1))
GROUP BY
tbl_Category.Category;
I know the answer must somehow include SELECT DISTINCT but am not able to get it to work. Do I need to create multiple subqueries?
This is really painful in MS Access. I think the following does what you want to do:
SELECT ac.Category, ac.num_activities, aco.num_comments
FROM (SELECT ca.category, COUNT(*) as num_activities
FROM (SELECT DISTINCT c.Category, a.ActivityID
FROM (tbl_Category as c INNER JOIN
tbl_Activity as a
ON c.CategoryID = a.CategoryID
) INNER JOIN
tbl_Comments as co
ON a.ActivityID = co.ActivityID
WHERE a.UnitID = 5 AND co.PeriodID = 1
) as caa
GROUP BY ca.category
) as ca LEFT JOIN
(SELECT c.Category, COUNT(*) as num_comments
FROM (SELECT DISTINCT c.Category, co.CommentId
FROM (tbl_Category as c INNER JOIN
tbl_Activity as a
ON c.CategoryID = a.CategoryID
) INNER JOIN
tbl_Comments as co
ON a.ActivityID = co.ActivityID
WHERE a.UnitID = 5 AND co.PeriodID = 1
) as aco
GROUP BY c.Category
) as aco
ON aco.CommentId = ac.CommentId
Note that your LEFT JOINs are superfluous because the WHERE clause turns them into INNER JOINs. This adjusts the logic for that purpose. The filtering is also very tricky, because it uses both tables, requiring that both subqueries have both JOINs.
You can use DISTINCT:
SELECT
tbl_Category.Category, Count(DISTINCT tbl_Activity.ActivityID) AS CountOfActivityID,
Count(DISTINCT tbl_Comments.CommentID) AS CountOfCommentID
FROM tbl_Category LEFT JOIN
(tbl_Activity LEFT JOIN tbl_Comments ON
tbl_Activity.ActivityID = tbl_Comments.ActivityID) ON
tbl_Category.CategoryID = tbl_Activity.CategoryID
WHERE
(((tbl_Activity.UnitID)=5) AND ((tbl_Comments.PeriodID)=1))
GROUP BY
tbl_Category.Category;

How do you work out the average of a sum function within a temp table?

I have created a temp table that lists each client's invoice(s), plus the number of days it took to pay the invoice. A client can have more than one invoice.
Instead of this, I would just like the temp table to list each client once, along with the AVERAGE number of days it took to pay all of their invoices.
Any tips on how to do this would be much appreciated.
Thanks
select
c.client_code,
b.bill_num,
b.bill_date,
ba.TRAN_DATE,
sum(datediff(Day,b.BILL_DATE, ba.TRAN_DATE)) as Days_To_Pay
into #tempG1
from blt_bill b
left outer join blt_billm bm on b.tran_uno = bm.bill_tran_uno
left outer join BLT_BILL_AMT ba on bm.BILLM_UNO = ba.BILLM_UNO
left outer join hbm_matter m on bm.matter_uno = m.matter_uno
left outer join hbm_client c on m.client_uno = c.client_uno
where b.total_bill_amt > 0.0
and bm.ar_status NOT IN ('P','X')
and ba.TRAN_TYPE in ('CR','crx')
group by c.client_code,b.bill_num,b.bill_date,ba.TRAN_DATE
select * from #tempG1
Drop Table #tempG1
I am not familiar with temp tables, but this should work (tested on a simliar scenario on MySQL8 and assuming that #tempG1 return results):
select
c.client_code,
b.bill_num,
b.bill_date,
ba.TRAN_DATE,
sum(datediff(Day,b.BILL_DATE, ba.TRAN_DATE)) as Days_To_Pay
from blt_bill b
left outer join blt_billm bm on b.tran_uno = bm.bill_tran_uno
left outer join BLT_BILL_AMT ba on bm.BILLM_UNO = ba.BILLM_UNO
left outer join hbm_matter m on bm.matter_uno = m.matter_uno
left outer join hbm_client c on m.client_uno = c.client_uno
where b.total_bill_amt > 0.0
and bm.ar_status NOT IN ('P','X')
and ba.TRAN_TYPE in ('CR','crx')
group by c.client_code,b.bill_num,b.bill_date,ba.TRAN_DATE
into #tempG1
############################
SELECT temp.client_code, AVG(temp.Days_To_Pay)
FROM (select * from #tempG1) as temp
GROUP BY temp.client_code
############################
#### Do you see results if drop? ####
Drop Table #tempG1
Note that I put #tempG1, at the bottom of your SELECT request, but might not be what want to achieve, not sure if you want to include your JOIN conditions or not.
Or you could do without temp table(including your join conditions):
SELECT temp.client_code, AVG(temp.Days_To_Pay)
(
select
c.client_code,
b.bill_num,
b.bill_date,
ba.TRAN_DATE,
sum(datediff(Day,b.BILL_DATE, ba.TRAN_DATE)) as Days_To_Pay
from blt_bill b
left outer join blt_billm bm on b.tran_uno = bm.bill_tran_uno
left outer join BLT_BILL_AMT ba on bm.BILLM_UNO = ba.BILLM_UNO
left outer join hbm_matter m on bm.matter_uno = m.matter_uno
left outer join hbm_client c on m.client_uno = c.client_uno
where b.total_bill_amt > 0.0
and bm.ar_status NOT IN ('P','X')
and ba.TRAN_TYPE in ('CR','crx')
group by c.client_code,b.bill_num,b.bill_date,ba.TRAN_DATE
) as temp
GROUP BY temp.client_code
This sounds like a simple aggregation:
select c.client_code, avg(datediff(Day, b.BILL_DATE, ba.TRAN_DATE)) as Days_To_Pay
from blt_bill b join
blt_billm bm
on b.tran_uno = bm.bill_tran_uno join
BLT_BILL_AMT ba
on bm.BILLM_UNO = ba.BILLM_UNO join
hbm_matter m on bm.matter_uno = m.matter_uno join
hbm_client c
on m.client_uno = c.client_uno
where b.total_bill_amt > 0.0 and
bm.ar_status not in ('P', 'X') and
ba.TRAN_TYPE in ('CR', 'crx')
group by c.client_code;
Note that you do not need outer joins. The where clause is turning most of them into inner joins anyway. Plus, if you are aggregating by the client code, then presumably you want a non-NULL value.

Trouble with aggregate in Where clause, Selecting Max(x) When Max(x) != 3

I am trying to reconfigure the below sql to only pull records when the Max(Field) != 3 but keep getting an error (detailed) below.
This is the code before adding the Where Max(field) != 3
SELECT P.Code,
MAX(PW.v1) AS V1
FROM SW
INNER JOIN S ON SW.S_Id = S.Id
INNER JOIN PW ON SW.PW_Id = PW.Id
INNER JOIN PON S.P_Id = P.id
WHERE S.P_Id = P.id
GROUP BY P.Code
My Attempt
SELECT P.Code,
MAX(PW.v1) AS V1
FROM SW
INNER JOIN S ON SW.S_Id = S.Id
INNER JOIN PW ON SW.PW_Id = PW.Id
INNER JOIN PON S.P_Id = P.id
WHERE S.P_Id = P.id
AND (SELECT MAX(PW.v1)
FROM SW AS SW2
WHERE SW.PWId = SW2.PW_Id) != 3
GROUP BY P.Code
This is the error I get and not sure what to do:
An aggregate may not appear in the WHERE clause unless it is in a subquery contained in a HAVING clause or a select list, and the column being aggregated is an outer reference.
Traditional method of filtering on results of an aggregate can be achieved by using HAVING clause. I also removed the unnecessary WHERE clause as you already joined those 2 tables on that column. Here is the query:
SELECT P.Code
,MAX(PW.v1) AS V1
FROM SW
INNER JOIN S
ON SW.S_Id = S.Id
INNER JOIN PW
ON SW.PW_Id = PW.Id
INNER JOIN P
ON S.P_Id = P.id
GROUP BY P.Code
HAVING MAX(PW.v1)!=3;

Left Outer Join returning results to be excluded

I have a query like
WITH a AS ( SELECT *
FROM inventory_tagalongs
WHERE TagAlong_ItemNum <> 'bokiwi2'
)
SELECT inventory.itemnum
, inventory.itemname
, inventory.ItemType,inventory.dept_id
FROM inventory
LEFT OUTER JOIN a
ON inventory.itemnum = a.itemnum
JOIN departments
ON inventory.dept_id = departments.dept_id
JOIN categories
ON departments.subtype = categories.cat_id AND categories.description = 'vapors'
in which i am trying to exclude a's results from the lower syntax but the results return are exactly the same whether or not left outer join..... is there or not.
Do i have my syntax incorrect here?
Good afternoon. Please try this:
select
inventory.itemnum
, inventory.itemname
, inventory.ItemType
, inventory.dept_id
from inventory
join departments on inventory.dept_id=departments.dept_id
join categories on departments.subtype=categories.cat_id and categories.description='vapors'
WHERE NOT EXISTS(
select 1 from inventory_tagalongs B where inventory.itemnum = b.itemnum
AND B.TagAlong_ItemNum <>'bokiwi2');
The CTE overthought the issue a bit. This accomplishes the goal with easier-to-read code and should perform better as well.
Thanks, John.
If you are trying to get results that don't have the tag, your logic is inverted. You want:
SELECT i.itemnum, i.itemname, i.ItemType, i.dept_id
FROM inventory i JOIN
departments d
ON i.dept_id = d.dept_id JOIN
categories c
ON d.subtype = c.cat_id AND
c.description = 'vapors' LEFT OUTER JOIN
a
ON i.itemnum = a.itemnum
WHERE a.itemnum IS NULL;
You don't need a CTE for this at all. It would usually be written as:
WITH a AS (
)
SELECT i.itemnum, i.itemname, i.ItemType, i.dept_id
FROM inventory i JOIN
departments d
ON i.dept_id = d.dept_id JOIN
categories c
ON d.subtype = c.cat_id AND
c.description = 'vapors' LEFT OUTER JOIN
inventory_tagalongs it
ON i.itemnum = it.itemnum AND it.TagAlong_ItemNum = 'bokiwi2'
WHERE a.itemnum IS NULL;
And -- as mentioned in another answer -- NOT EXISTS is another typical way to approach this problem.