When a SQL Server query returns no rows(NOT null rows) how do you include that in an aggregate function? - sql-server

I'm writing a query to look for courses that do not have any of its gradable items graded.
In Blackboard when a user doesn't have a grade at all(e.g. no attempt was ever made) there simply isn't a row in the table gradebook_grade
So if a course doesn't have any grades at all the gradebook_grade table does not have any rows referencing the primary key of the Blackboard course_id
This is what I've used so far:
use bblearn
select cm.course_id
from course_main cm
join gradebook_main gbm on cm.pk1 = gbm.crsmain_pk1
join gradebook_grade gbg on gbm.pk1 = gbg.gradebook_main_pk1
where cm.pk1 = 3947
group by cm.course_id
having count(gbg.pk1) <= 0
The course in question(pk1 3947) is confirmed to not have any grades. So SQL Server says 0 rows affected, naturally.
The thing is, it doesn't select the course_id. I'm guessing having doesn't account for blank/non-existent rows. Is there a way to structure the query to get the course ID when there isn't anything returned? Am I joining on the wrong columns or using where on the wrong column? Any help is appreciated.

Use a left join
select cm.course_id
from course_main cm
left join gradebook_main gbm on cm.pk1 = gbm.crsmain_pk1
left join gradebook_grade gbg on gbm.pk1 = gbg.gradebook_main_pk1
where cm.pk1 = 3947
group by cm.course_id

Related

SQL Server Index over lookup table of distinct values

I am trying to speed up the following SQL Server query:
SELECT
V.Id, V.Number, V.VisitDate, V.ArrivalTime, V.VisitKindId, VK.Description AS
VisitKindDescription,
VK.DescriptionAr AS VisitKindDescriptionAr, V.StatusId, V.Note, V.CancelingReason,
V.CancelingTime, V.EnterToDoctorRoomTime,
V.PatientId, P.Number AS PatientNumber, P.FirstName, P.LastName, P.BirthDate, P.Note AS
PatientNotes, V.DoctorId, D.FullName AS DoctorFullName, V.CreatedById,
U.FullName AS UserFullName, V.CreationDate, V.VersionNo
FROM
Patient_Tbl P INNER JOIN
Visit_Tbl V ON P.Id = V.PatientId INNER JOIN
VisitKind_Tbl VK ON V.VisitKindId = VK.Id INNER JOIN
Doctor_Tbl D ON V.DoctorId = D.Id INNER JOIN
User_Tbl U ON V.CreatedById = U.Id INNER JOIN
VisitStatus_Tbl VS ON V.StatusId = VS.Id
WHERE V.StatusId = 2 --patient is in doctor room
and we had the following 4 values the VisitStatus_Tbl:
(1 -> In Waiting Room, 2 -> In Doctor Room, 3 -> Canceled, 4 -> Completed)
and in one moment of time, there is only one record on the Visits table for one person in the doctor's room.
The end-user inform me that there is a delay in the use case that depends on the above query.
Please help us speed system performance by suggesting the proper index.
Thanks,
You do not indicate if you have any indexes on the tables now. I will assume that the 'ID' columns for patient_tbl, etc are clustered primary keys or just primary keys and have indexes. If not, that is another problem.
Simple rule: start with index foreign keys (lookup tables) and WHERE clauses.
CREATE INDEX ix_visit_tbl_statusid ON visit_tbl(statusId)
CREATE INDEX ix_visit_tbl_patientid ON visit_tbl(patientId)
CREATE INDEX ix_visit_tbl_visitkindId ON visit_tbl(visitkindId)
CREATE INDEX ix_visit_tbl_doctorid ON visit_tbl(doctorId)
CREATE INDEX ix_visit_tbl_createdbyid ON visit_tbl(createdbyId)
Now for the comments on how that is too many indexes. It depends ...

RIGHT\LEFT Join does not provide null values without condition

I have two tables one is the lookup table and the other is the data table. The lookup table has columns named cycleid, cycle. The data table has SID, cycleid, cycle. Below is the structure of the tables.
If you check the data table, the SID may have all the cycles and may not have all the cycles. I want to output the SID completed as well as missed cycles.
I right joined the lookup table and retrieved the missing as well as completed cycles. Below is the query I used.
SELECT TOP 1000 [SID]
,s4.[CYCLE]
,s4.[CYCLEID]
FROM [dbo].[data] s3 RIGHT JOIN
[dbo].[lookup_data] s4 ON s3.CYCLEID = s4.CYCLEID
The query is not displaying me the missed values when I query for all the SID's. When I specifically query for a SID with the below query i am getting the correct result including the missed ones.
SELECT TOP 1000 [SID]
,s4.[CYCLE]
,s4.[CYCLEID]
FROM [dbo].[data] s3 RIGHT JOIN [dbo].[lookup_data] s4
ON s3.CYCLEID = s4.CYCLEID
AND s3.SID = 101002
ORDER BY [SID], s4.[CYCLEID]
As I am supplying this query into tableau I cannot provide the sid value in the query. I want to return all the sid's and from tableau I will be do the rest of the things.
The expected output that i need is as shown below.
I wrote a cross join query like below to acheive my expected output
SELECT DISTINCT
tab.CYCLEID
,tab.SID
,d.CYCLE
FROM ( SELECT d.SID
,d.[CYCLE]
,e.CYCLEID
FROM ( SELECT e.sid
,e.CYCLE
FROM [db_temp].[dbo].[Sheet3$] e
) d
CROSS JOIN [db_temp].[dbo].[Sheet4$] e
) tab
LEFT OUTER JOIN [db_temp].[dbo].[Sheet3$] d
ON d.CYCLEID = tab.CYCLEID
AND d.SID = tab.SID
ORDER BY tab.SID
,tab.CYCLEID;
However I am not able to use this query for more scenarios as my data set have nearly 20 to 40 columns and i am having issues when i use the above one.
Is there any way to do this in a simpler manner with only left or right join itself? I want the query to return all the missing values and the completed values for the all the SID's instead of supplying a single sid in the query.
You can create a master table first (combine all SID and CYCLE ID), then right join with the data table
;with ctxMaster as (
select distinct d.SID, l.CYCLE, l.CYCLEID
from lookup_data l
cross join data d
)
select d.SID, m.CYCLE, m.CYCLEID
from ctxMaster m
left join data d on m.SID = d.SID and m.CYCLEID = d.CYCLEID
order by m.SID, m.CYCLEID
Fiddle
Or if you don't want to use common table expression, subquery version:
select d.SID, m.CYCLE, m.CYCLEID
from (select distinct d.SID, l.CYCLE, l.CYCLEID
from lookup_data l
cross join data d) m
left join data d on m.SID = d.SID and m.CYCLEID = d.CYCLEID
order by m.SID, m.CYCLEID

Why do I have duplicate records in my JOIN

I am retrieving data from table ProductionReportMetrics where I have column NetRate_QuoteID. Then to that result set I need to get Description column.
And in order to get a Description column, I need to join 3 tables:
NetRate_Quote_Insur_Quote
NetRate_Quote_Insur_Quote_Locat
NetRate_Quote_Insur_Quote_Locat_Liabi
But after that my premium is completely off.
What am I doing wrong here?
SELECT QLL.Description,
QLL.ClassCode,
prm.NetRate_QuoteID,
QL.LocationID,
ISNULL(SUM(premium),0) AS NetWrittenPremium,
MONTH(prm.EffectiveDate) AS EffMonth
FROM ProductionReportMetrics prm
LEFT JOIN NetRate_Quote_Insur_Quote Q
ON prm.NetRate_QuoteID = Q.QuoteID
INNER JOIN NetRate_Quote_Insur_Quote_Locat QL
ON Q.QuoteID = QL.QuoteID
INNER JOIN NetRate_Quote_Insur_Quote_Locat_Liabi QLL
ON QL.LocationID = QLL.LocationID
WHERE YEAR(prm.EffectiveDate) = 2016 AND
CompanyLine = 'Ironshore Insurance Company'
GROUP BY MONTH(prm.EffectiveDate),
QLL.Description,
QLL.ClassCode,
prm.NetRate_QuoteID,
QL.LocationID
I think the problem in this table:
What Am I missing in this Query?
select
ClassCode,
QLL.Description,
sum(Premium)
from ProductionReportMetrics prm
LEFT JOIN NetRate_Quote_Insur_Quote Q ON prm.NetRate_QuoteID = Q.QuoteID
LEFT JOIN NetRate_Quote_Insur_Quote_Locat QL ON Q.QuoteID = QL.QuoteID
LEFT JOIN
(SELECT * FROM NetRate_Quote_Insur_Quote_Locat_Liabi nqI
JOIN ( SELECT LocationID, MAX(ClassCode)
FROM NetRate_Quote_Insur_Quote_Locat_Liabi GROUP BY LocationID ) nqA
ON nqA.LocationID = nqI.LocationID ) QLL ON QLL.LocationID = QL.LocationID
where Year(prm.EffectiveDate) = 2016 AND CompanyLine = 'Ironshore Insurance Company'
GROUP BY Q.QuoteID,QL.QuoteID,QL.LocationID
Now it says
Msg 8156, Level 16, State 1, Line 14
The column 'LocationID' was specified multiple times for 'QLL'.
It looks like DVT basically hit on the answer. The only reason you would get different amounts(i.e. duplicated rows) as a result of a join is that one of the joined tables is not a 1:1 relationship with the primary table.
I would suggest you do a quick check against those tables, looking for table counts.
--this should be your baseline count
SELECT COUNT(*)
FROM ProductionReportMetrics
GROUP BY MONTH(prm.EffectiveDate),
prm.NetRate_QuoteID
--this will be a check against the first joined table.
SELECT COUNT(*)
FROM NetRate_Quote_Insur_Quote Q
WHERE QuoteID IN
(SELECT NetRate_QuoteID
FROM ProductionReportMetrics
GROUP BY MONTH(prm.EffectiveDate),
prm.NetRate_QuoteID)
Basically you will want to do a similar check against each of your joined tables. If any of the joined tables are part of the grouping statement, make sure they are also in the grouping of the count check statement. Also make sure to alter the WHERE clause of the check count statement to use the join clause columns you were using.
Once you find a table that returns the incorrect number of rows, you will have your answer as to what table is causing the problem. Then you will just have to decide how to limit that table down to distinct rows(some type of aggregation).
This advice is really just to show you how to QA this particular query. Break it up into the smallest possible parts. In this case, we know that it is a join that is causing the problem, so take it one join at a time until you find the offender.

Linq Left outer join Query

How to convert this sql query into Linq query?
select *
from setupUOMs as su
LEFT OUTER JOIN scmSKUUoMs as ssu
on su.UoMID != ssu.UoMID
where ssu.SKUID = 446 and su.UMTypeID = 5
Following is the linq query.
from c in setupUOMs
join o in scmSKUUoMs
on c.UOMID equals o.UoMID into sr
from x in sr.DefaultIfEmpty()
where x.SKUID == 446
select x
In above query I've so far only done to extract the join number but what I want is to select the non equal records of left table but I'm able to show joined record. and it returns the records of only left table while my result is based on both columns. In where clause I can access x.SKUID which is from left table but can't access x.UMTypesID which is form right table (Means no column from right table is returned on which I can make condition).
Take a look at this LEFT JOIN LINQTOSQL Example.
http://www.devcurry.com/2011/01/linq-left-join-example-in-c.html
Maybe it will help.
A join with a non-equals condition is not a join but a Cartesian product excluding the inner joined records. There may be valid reasons to make a query as you did, but there's always a risk of blowing up the result set in terms of number of records.
So let's assume the conditions ensure a result that makes sense, then you can do:
from su in setupUOMs
from ssu in scmSKUUoMs // from .. from is a Cartesian product
where su.UOMID != ssu.UoMID
&& ssu.SKUID == 446
&& su.UMTypeID == 5
select new { su.Prop1, ssu.Prop2, ... }

formulating an SSRS query to include zero/empty rows in a GROUP BY

I'm working on a SSRS report and I'm having an issue with my Plant name not showing when there is no data available for the date range selected.
The far left column, first row (technically the 2nd by the image) is where my plant name should appear at all times:
Essentially the first image showed just my blank rows/columns. The first column, first row is where my plant name should be at all times. The remaining columns are my returned data based on date selection.
The second image would show everything working as it should when there is data.
I'm grouping by PlantCode in SSRS which is what gives my my plant name. I don't know how to get the plant name to appear even if there is not data available.
Is this possible?
I THOUGHT I could use something like iif(salesvolume is NOTHING, [PlantCODE],[PlantCode])
Here is the database query for the report
SELECT
PInv.[Plant_Numer],
PInv.[Plant_Code],
PInv.{Department_number],
PInv.[Inventory_Volume],
Pinv.[Inventory_Date], -- 'Last Inventory Date'
pls.[Actual_Volume],
pls.[Budget_Volume],
ppf.[Good_Output_Product_Units] AS 'Production Volume', -- 'Next Day Production
CASE
WHEN coalesce (pls.[Acutal_Volume],0) = 0 and coalesce (pls.[Actual_Sales_Dollars],0) = 0 THEN 0
ELSE ((pls.[Actual_Sales_Dollars/pls.[Actual_Volume])) AS 'Average Price' -- 'Next Day Sales'
FROM
[TrueOpportunity].[dbo].[Production_Fact] pf
inner join [TrueOpportunity].[dbo].[Production_Process_Fact] ppf on ppf.production_number = pf.production_number
inner join [TrueOpportunity].[dbo].[Process] prc on prc.process_number = pf.process_number
inner join [TrueOpportunity].[dbo].[Department] dpt on dpt.department_number = prc.department_number
inner join [WoodProduction_New].[dbo].[Plywood_Layup_Sales] pls on pls.procesS_number = pf.procesS_number
inner join [WoodProduction_New].[dbo].[Process_Inventory] Pinv on PInv.[Inventory_Date] = pf.date
and pls.product_date = pf.date
and dpt.department_number = pinv.department_number
WHERE
pf.date between #BeginningDate and #EndingDate
I think you want to change your query so that Process Inventory is your primary table and all other tables are LEFT JOINED to that table. That way the Plant Number & Code will show up regardless of whether there is matching data in the other tables.
This syntax is probably not completely correct, but I would start out by changing your FROM clause to look something like this:
FROM
[WoodProduction_New].[dbo].[Process_Inventory] Pinv
LEFT JOIN [TrueOpportunity].[dbo].[Production_Fact] pf
ON PInv.[Inventory_Date] = pf.date
LEFT JOIN [TrueOpportunity].[dbo].[Production_Process_Fact] ppf
ON ppf.production_number = pf.production_number
LEFT JOIN [TrueOpportunity].[dbo].[Process] prc
ON prc.process_number = pf.process_number
LEFT JOIN [TrueOpportunity].[dbo].[Department] dpt
ON dpt.department_number = prc.department_number
AND dpt.department_number = pinv.department_number
LEFT JOIN [WoodProduction_New].[dbo].[Plywood_Layup_Sales] pls
ON pls.process_number = pf.process_number
AND pls.product_date = pf.date
Experiment with that and see if you can get it to display the data that you want.

Resources