Why is this SQL case statement behaving like an OR statement? - sql-server

Consider the following query:
declare #RentalId int = 1
SELECT
r.RentalId
,r.[Name]
,rt.TypeId
FROM dbo.Rental r
LEFT JOIN dbo.RentalRateType rt ON (
r.RentalId = rt.RentalId
AND rt.TypeId = (
case when rt.TypeId = 6 and coalesce(rt.[Max], rt.[Min]) is not null then 6
when rt.TypeId = 1 and coalesce(rt.[Max], rt.[Min] is not null then 1
else -1 end
))
WHERE r.RentalId = #RentalId
I'm attempting to return a single record/row. The particular rental in question has 2 records in the dbo.RentalRateType table, and when I run the above query, I get 2 results, but I want it to short circuit on the first match in the case where.
Basically, the end user can fill in multiple rate types, more than what you see in this example, and each of those types has a priority. 6 is the highest priority in the example.
So I'm getting this result:
RentalId | Name | TypeId
----------------------------
1 Super Car 6
1 Super Car 1
But if the type (6) exists, I would expect only the first row above returned.
I must be missing something silly. This works as expected:
case when 1=2 then 6
when 1=1 then 1
else -1 end
While I'm here, I'm open to a more efficient manner of handling this if exists.

Use an apply instead, these are an efficient way to get "top n" queries:
SELECT
r.RentalId
, r.[Name]
, oa.TypeId
FROM dbo.Rental r
OUTER APPLY (
SELECT TOP (1)
rt.TypeId
FROM dbo.RentalRateType rt
WHERE r.RentalId = rt.RentalId
ORDER BY
rt.TypeId DESC
) oa
WHERE r.RentalId = #RentalId

Related

Check if table has specific row value

I have two tables called game and gameprogress. Gameprogress has a column called state that describes progress. Game is related to gameprogress trough an id.
Possible states is: 1,3,4 in gameprogress.
I want to find games without a state = 1 and without a state = 4. I've tried something like this:
select top 10 gp.gameid, count(gp.state) as dup
from gameprogress gp
join game g on g.id= gp.gameid
where g.gamestate != 2 and gp.state != 1
group by gp.gameid
having count(gp.state)>1
You can use CASE EXPRESSION in the HAVING clause :
SELECT TOP 10 g.gameid
FROM game g
LEFT JOIN gameprogress gp
ON g.id= gp.gameid
GROUP BY g.gameid
HAVING COUNT(CASE WHEN gp.state = 3 THEN 1 END) = COUNT(*)

SQL Union Count to Sum Data

I have a bit of a complex query .... I need to do an update statement on the summation of two union-ed SQL queries (problem is the data in the queries isn't numeric so i'm counting rows instead of summing values) but I then need to sum those rows.
UPDATE #LT_Actuals_TEMP
SET pCount = h.countPerfs
FROM (
select count(distinct c.perf_description) as countPerfs, b.program, b.Prog_id
from #LT_Actuals_TEMP TableP
where a.Performances = 'Y' and a.current_inactive = 0
group by b.Program, b.Prog_id
union
select distinct count(p.perf_code) as countPerfs, x.value, b.Prog_id
from T_PERF p
where x.content_type = 23
group by x.value, b.Prog_id
) h where h.Prog_id = #LT_Actuals_TEMP.program_id
the first query data comes back as such
countPerfs program Prog_id
7 Name 31
and second query comes back as
countPerfs program Prog_id
1 Name 31
what I need pCount to be set to at the end of the day is 8
Expected results
when I do select * from #LT_Actuals_TEMP
I see the value
8 for the Program Name, Id 31
You can solve it by adding another level in the from part where you sum up the data returned from the union.
Your query seems to be missing some source tables (as there are aliases used that don't point to anything) so I guess you're removed some parts, but in general it should look something like this:
UPDATE #LT_Actuals_TEMP
SET pCount = h.sum_of_countperfs
FROM (
select program, prog_id, sum(countPerfs) as sum_of_countperfs
from (
select count(distinct c.perf_description) as countPerfs, b.program, b.Prog_id
from #LT_Actuals_TEMP TableP
where a.Performances = 'Y' and a.current_inactive = 0
group by b.Program, b.Prog_id
union all
select distinct count(p.perf_code) as countPerfs, x.value, b.Prog_id
from T_PERF p
where x.content_type = 23
group by x.value, b.Prog_id
) as sub_q group by program, prog_id
) h where h.Prog_id = #LT_Actuals_TEMP.program_id
Also, you probably want to use union all so that duplicates are not removed.

CTE goes recursive and fails

I have created a very small CTE recursive function but seems it goes recursive all the time and fails.
Please find details inline:
Table Name & Data:
insert into dbo.HierarchyEmployee
Values
(1,1, 'Name1'),
(2,1, 'Name2'),
(3,4, 'Name3'),
(4,5, 'Name4'),
(5,2, 'Name5'),
(6,4, 'Name6'),
(7,1, 'Name7'),
(8,8, 'Name8'),
(9,3, 'Name9'),
(10,1, 'Name10')
Ideal Result Set:
HierarchyParent HierarchyID Name
1 1 Name
1 2 Name2
1 7 Name7
1 10 Name10
I tried to achieve this through reclusive CTE as I wanted to look how recursion works.
Below is the query used:
WITH CTE_HierarchyEmployee
AS
(
SELECT H.HierarchyID,
H.HierarchyParent,
H.Name
FROM dbo.HierarchyEmployee H
WHERE H.HierarchyID = 1
UNION ALL
SELECT H.HierarchyID,
H.HierarchyParent,
H.Name
FROM
dbo.HierarchyEmployee H
INNER JOIN CTE_HierarchyEmployee CTEH
ON H.HierarchyParent = cteh.HierarchyID
)
SELECT * FROM CTE_HierarchyEmployee
Error:
Msg 530, Level 16, State 1, Line 41
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
Appreciate you input on how to resolve the revulsion.
EDIT: I figure out the reason. Problem is in the sample data. The parent most HierarchyID = 1 is also having the ParentId = 1. In "NORMAL" situation this can't be the case, most of the times parentID of top most id is NULL. Since parent and HierarchyID are same (1) it is going into the loop of 1 is parent of 1.
2 ways you can solve the problem:
1. Update parentID of HierarchyID = 1 to NULL
2. Add extra where condition in Recursive query where H.HierarchyID <> 1
You just need to add OPTION (MAXRECURSION 0) at the end of the query to over come the limitation of 100 recursion. See:
WITH CTE_HierarchyEmployee
AS
(
SELECT H.HierarchyID,
H.HierarchyParent,
H.Name
FROM dbo.HierarchyEmployee H
WHERE H.HierarchyID = 1
UNION ALL
SELECT H.HierarchyID,
H.HierarchyParent,
H.Name
FROM
dbo.HierarchyEmployee H
INNER JOIN CTE_HierarchyEmployee CTEH
ON H.HierarchyParent = cteh.HierarchyID
)
SELECT * FROM CTE_HierarchyEmployee
OPTION (MAXRECURSION 0)

Recursive triggers and error : subquery returned more than 1 value

I need your help !
I am working on sql server
1-- I created this trigger but it seems to be wrong...
CREATE TRIGGER [dbo].[chargeAZero]
ON [dbo].[situations_final]
after INSERT
AS
BEGIN
SET nocount ON
UPDATE sfinal
SET charge = 00
FROM inserted i
INNER JOIN situations_final sfinal
ON i.referencepiece = sfinal.referencepiece
AND i.ancienposte = sfinal.ancienposte
AND i.numerophase = sfinal.numerophase
AND i.datestrategie = sfinal.datestrategie
/*and i.Datecadence=sfinal.Datecadence*/
WHERE (SELECT sfinal.nouveauposte
FROM situations_final sfinal
INNER JOIN inserted i
ON i.referencepiece = sfinal.referencepiece
AND i.ancienposte = sfinal.ancienposte
AND i.numerophase = sfinal.numerophase
AND i.datestrategie = sfinal.datestrategie) IS
NULL
END
The error message is always the same: the subquery returned more than one value... I think I wrote my trigger correctly as I did with others that work fine.
2-- My second question is : Is it possible to make only one trigger recursive ?
3-- As you have noticed on my database on the table "Nomenclatures" (Bill of materials in english) I have 3 elements:
*codepiecemere: The component mother
*codepiecefille: the component child
* the quantity.
I give you an example of what I need :
Mother= A Child= B Quantity= 2
Mother= B Child= C Quantity= 3
I want a trigger to give me a result like that:
A 1 B 2 C 6=2*3 (the quantity needed of C to make 1 B).
Thank you very much
Here's a recursive query that solves the material aggregation problem.
Table definition
CREATE TABLE [dbo].[Material](
[Mother] [varchar](100) NOT NULL,
[Child] [varchar](100) NOT NULL,
[Quantity] [int] NOT NULL,
)
and the query:
WITH Result(mother, child, quantity)
AS
(
select * from material
union all
select M.mother, R.Child, M.quantity * R.Quantity as Quantity
from Result R INNER JOIN Material M ON M.Child = R.Mother
)
select * from result
You can see an example here: http://sqlfiddle.com/#!6/6dc64/1
UPDATE:
Sql fiddle is not working, I don't know why
UPDATE 2
Sql Fiddle is back! :-)
The is null is not normally used with subqueries. Try this:
where not exists (select 1
from SITUATIONS_Final sfinal inner join inserted i
on i.ReferencePiece=sfinal.ReferencePiece
and i.AncienPoste=sfinal.AncienPoste
and i.numerophase=sfinal.numerophase
and i.datestrategie=sfinal.datestrategie
)
This is assuming that the is null is testing for no values being returned, as opposed to a NULL value in sfinal.nouveauposte. If the latter:
where exists (select 1
from SITUATIONS_Final sfinal inner join inserted i
on i.ReferencePiece=sfinal.ReferencePiece
and i.AncienPoste=sfinal.AncienPoste
and i.numerophase=sfinal.numerophase
and i.datestrategie=sfinal.datestrategie
where sfinal.nouveauposte is null
)
EDIT:
Do you need the subquery at all?
UPDATE sfinal
SET charge = 00
FROM inserted i
INNER JOIN situations_final sfinal
ON i.referencepiece = sfinal.referencepiece
AND i.ancienposte = sfinal.ancienposte
AND i.numerophase = sfinal.numerophase
AND i.datestrategie = sfinal.datestrategie
WHERE sfinal.nouveauposte IS NULL;
I think the problem is that you are inserting more than one row in a single command, so the inserted table contains more than one row. As a consequence the sub query
SELECT sfinal.nouveauposte
FROM situations_final sfinal
INNER JOIN inserted i
ON i.referencepiece = sfinal.referencepiece
AND i.ancienposte = sfinal.ancienposte
AND i.numerophase = sfinal.numerophase
AND i.datestrategie = sfinal.datestrategie
contains more than one row too and cannot be compared to NULL that is a scalar value.
I am ambitious :D I tried to improve the script:
WITH RESULT (MOTHER, CHILD, QUANTITY)
as
(
select Mother, Child, CONVERT(Numeric(10,0), Quantity) as Quantity from bilangammestest
union all select M.mother, R.Child, CONVERT(Numeric(10,0), M.quantity * R.Quantity) as Quantity from Result R
INNER JOIN bilangammestest M ON M.Child = R.Mother
)
select * from result
where mother not in (select child from bilangammestest )
Here are the data I have on my table "Bilangammestest":
Z A 1
Z Y 1
A B 2
Y B 2
B C 3
Here are the result I get :
Z A 1
Z Y 1
Z C 6
Z C 6
Z B 2
Z B 2
Here is the Final result I want:
Z A 1
Z Y 1
Z C 12
Z B 4
I tried to do a 'sum' but I couldn't do it correctly :(

SQL query - need to exclude if Requirement NOT met, and exclude if Disqualifier IS met

I have a feeling once i see the solution i'll slap my forehead, but right now I'm not seeing it.
I have a lookup table, say TableB, which looks like this. All fields are INT except the last two which are BOOL.
ID, TableA_ID, Value, Required, Disqualifies
I have a list of TableA_Id values (1, 2, 3 ) etc
For each record in this table, either Required can be true or disqualified can be true - they cant both be true at the same time. They can both be false or null though. There can be duplicate values of TableA_Id but there should never be duplicates of TableA_Id and Value
If required is true for any of those TableA_ID values, and none of those values are in my list, return no records. If none of the values are marked as required (required = 0 or null) then return records UNLESS any of the values are marked as Disqualifies and are in the list, in which case i want to return no records.
So - if a field is required and i dont have it, dont return any records. If a field is marked as disqualified and i have it, don't return any records. Only return a record if either i have a required value or don't have a disqualified value or there are no required values.
I hope I explained myself clearly.
Thanks in advance for pointing me in the right direction.
As an example of what my records might look like:
ID TableA_ID Value Required Disqualifies
-- --------- ----- -------- ------------
1 123 1 True False
2 123 2 True False
3 123 3 False False
4 123 4 False True
5 456 1 False True
6 456 2 False False
Given this set of sample data, if we're working with TableA_Id 123 and my list of values is lets say 1 and 3, i would get data returned because i have a required value and dont have any disqualified values. If my list of values were just 3, i'd get no records since i'm missing of the Required values. If my list of values were 1 and 4, i'd get no records because 4 is marked as disqualified.
Now if we're working with TableA_Id 456, the only list of values that would return any records is 2.
Maybe i should post the whole SQL query - i was trying to keep this short to make it easier for everyone, but it looks like maybe that's not working so well.
Here is the full dynamically generated query. The bit i am working on now is the 2nd line from the bottom. To equate this to my example, t.id would be TableA_ID, Value would be PDT_ID.
SELECT DISTINCT t.ID, t.BriefTitle, stat.Status, lstat.Status AS LocationStatus, st.SType, t.LAgency, l.City, state.StateCode
,( SELECT TOP 1 UserID
FROM TRecruiter
WHERE TrialID = t.ID AND Lead = 1 ), l.ID as LocationID
, l.WebBased
FROM Trial t
INNER JOIN Location l ON t.ID = l.TrialID
FULL JOIN pdt on t.ID = pdt.trialid
FULL JOIN pdm on t.ID = pdm.TrialID
FULL JOIN s on t.ID = s.TrialID
FULL JOIN hy on t.ID = hy.TrialID
FULL JOIN ta on t.ID = ta.TrialID
FULL JOIN stt on t.ID = stt.TrialID
FULL JOIN [Status] stat ON t.StatusID = stat.ID
FULL JOIN st ON t.StudyTypeID = st.ID
FULL JOIN State state ON l.StateID = state.ID
FULL JOIN [Status] lstat ON l.StatusID = lstat.ID
FULL JOIN ts ON t.ID = ts.TrialID
FULL JOIN tpdm ON t.ID = tpdm.TrialID
WHERE ((t.ID IS NOT NULL)
AND (EligibleHealthyVolunteers IS NULL OR EligibleHealthyVolunteers = 1 OR (0 = 0 AND EligibleHealthyVolunteers = 0))
AND (eligiblegenderid is null OR eligiblegenderid = 1 OR eligiblegenderid = 3)
AND ((EligibleMinAge <= 28 AND EligibleMaxAge >= 28) OR (EligibleMinAge <= 28 AND EligibleMaxAge is null) OR (EligibleMinAge IS NULL AND EligibleMaxAge >= 28))
AND (HYID = 6 AND (hy.Disqualify = 0 OR hy.Disqualify IS NULL AND NOT EXISTS (SELECT * FROM hy WHERE t.id = hy.TrialID AND hy.Req =1)) OR HYID = 6 AND hy.req = 1)
AND (PDT_ID IN (1) AND ( pdt.Disqualify = 0 OR pdt.Disqualify IS NULL AND NOT EXISTS (select * from pdt where t.id = pdt.TrialID AND pdt.Req = 1)) OR PDT_ID IN (1) AND (pdt.Req = 1 AND (pdt.Disqualify = 0 or pdt.Disqualify is null )))
) AND ((3959 * acos(cos(radians(34.18)) * cos(radians(l.Latitude)) * cos(radians(l.Longitude) - radians(-118.46)) + sin(radians(34.18)) * sin(radians(l.Latitude)))) <= 300 OR l.Latitude IS NULL) AND t.IsPublished = 1 AND (t.StatusID = 1 OR t.StatusID = 2)
I've changed/shortened some table names just for security/privacy reasons.
Edit:
I think i am close to getting this working, but I'm getting tripped up on the logic again.
I have the following bit of sql:
AND ( exists (SELECT * FROM pdt WHERE Req = 1 AND trialid = t.id AND pdT_ID IN (2) ) AND EXISTS (SELECT * FROM pdt WHERE Req = 1 AND trialid = t.id ) )
I'm not sure how to structure this. Those two exists statement should make the whole thing true in the following combination:
True & False
True & True
False & False
If it's False & True, then the whole thing is false. In other words if there is a Req =1 AND the PDT_ID that is marked as Req=1 is not in our list (in the example above the list just contains '2') then return false.
EDIT:
I think i finally got it.
AND NOT EXISTS (SELECT * FROM pdt WHERE Disqualify = 1 AND trialid = t.id AND PDT_ID IN (2) )
AND NOT ( NOT exists (SELECT * FROM pdt WHERE Req = 1 AND trialid = t.id AND PDT_ID IN (2) ) AND EXISTS (SELECT * FROM pdt WHERE Req = 1 AND trialid = t.id ) )
So far this seems to work in testing. Although I'm only working with two values of PDT_ID. If this does resolve my problem, i will come back and give someone the credit for helping me.
SELECT *
FROM TABLEB B
WHERE
(
B.REQUIRED = 1
AND EXISTS
(
SELECT 1
FROM TABLEA A
WHERE A.ID =B.TABLEA_ID
)
)
OR
(
B.REQUIRED != 1
AND B.DISQUALIFIES <> 1
)
OR
(
B.REQUIRED != 1
AND B.DISQUALIFIES = 1
AND EXISTS
(
SELECT 1
FROM TABLEA A
WHERE A.ID =B.TABLEA_ID
)
)
UPDATE - after the EDIT and explanation from OP:
Change the line
FULL JOIN pdt on t.ID = pdt.trialid
To
FULL JOIN (SELECT * FROM pdt BB WHERE
BB.TrialID IN (SELECT AA.ID FROM Trial AA WHERE AA.ID = BB.TrialID) AND
1 > (SELECT COUNT(*) FROM Trial A
LEFT OUTER JOIN pdt B ON B.Req != 1 AND B.Disqualify != 1 AND B.TrialID = A.ID
WHERE B.TrialID IS NULL)) pdt ON t.ID = pdt.TiralID
AND change the line before last from
AND (PDT_ID IN (1) AND ( pdt.Disqualify = 0 OR pdt.Disqualify IS NULL AND NOT EXISTS (select * from pdt where t.id = pdt.TrialID AND pdt.Req = 1)) OR PDT_ID IN (1) AND (pdt.Req = 1 AND (pdt.Disqualify = 0 or pdt.Disqualify is null )))
To
AND PDT_ID IN (1)
(You seem to have found a solution, yet I've decided to share my thoughts about this problem anyway.)
Given you've got a set of TableA IDs, each of which is accompanied by a set of some values, and you want to test the entire row set against this TableB thing using the rules you've set forth, I think the entire checking process might look like this:
Match every pair of TableA.ID and Value against TableB and get aggregate maximums of Required and Disqualifies for every TableA.ID along the way.
Derive a separate list of TableA_ID values with their corresponding maximum values of Required, from TableB. That will be for us to know whether a particular TableA_ID must have a required value at all.
Match the row set obtained at Stage 1 against the derived table (Stage 2) and check the aggregate values:
1) if the actual aggregate Disqualifies for a TableA_ID is 1, discard this TableA_ID set;
2) if a TableA_ID has a match in the Stage 2 derived table and the aggregate maximum of Required that we obtained at Stage 1 doesn't match the maximum Required in the derived table, discard the set as well.
Something tells me that it would be better at this point to move on to some sort of illustration. Here's a sample script, with comments explaining which part of the script implements which part of the description above:
;
WITH
/* this is the row set to be tested and which
is supposed to contain TableA.IDs and Values */
testedRowSet AS (
SELECT
TableA.ID AS TableA_ID,
SomethingElse.TestedValue AS Value,
...
FROM TableA
JOIN SomethingElse ON some_condition
...
),
/* at this point, we are getting the aggregate maximums
of TableB.Required and TableB.Disqualifies for every
TableA_ID in testedRowSet */
aggregated AS (
SELECT
testedRowSet.TableA_ID,
testedRowSet.Value,
...
DoesHaveRequiredValues = MAX(CASE TableB.Required WHEN 1 THEN 1 ELSE 0 END) OVER (PARTITION BY testedRowSet.TableA_ID),
HasDisqualifyingValues = MAX(CASE TableB.Disqualifies WHEN 1 THEN 1 ELSE 0 END) OVER (PARTITION BY testedRowSet.TableA_ID)
FROM testedRowSet
LEFT JOIN TableB ON testedRowSet.TableA_ID = TableB.TableA_ID
AND testedRowSet.Value = TableB.Value
),
/* this row set will let us see whether a particular
TableA_ID must have a required value */
properties AS (
SELECT
TableA_ID,
MustHaveRequiredValues = MAX(CASE Required WHEN 1 THEN 1 ELSE 0 END)
FROM TableB
GROUP BY TableA_ID
),
/* this is where we are actually checking the previously
obtained aggregate values of Required and Disqualifies */
tested AS (
SELECT
aggregated.TableA_ID,
aggregated.Value,
...
FROM aggregated
LEFT JOIN properties ON aggregated.TableA_ID = properties.TableA_ID
WHERE aggregated.HasDisqualifyingValues = 0
AND (properties.TableA_ID IS NULL
OR properties.MustHaveRequiredValues = aggregated.DoesHaveRequiredValues)
)
SELECT * FROM tested

Resources