Determine Group By from Parameter - - tsql

How is it that this is valid:
ALTER PROCEDURE [StoredProcedure]
#abcID int = null -- optional param
SELECT columnJ, columnK, Count(eID) AS Num, Sum(OutXYZ) as TotalProdXYZ, Sum(RawXYZ) as TotalRawXYZ
FROM [v_ViewTable]
WHERE (#abcID IS NULL OR (abcID = #abcID))
GROUP BY columnJ, columnK
But then this is throwing a "columnJ is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."
ALTER PROCEDURE [StoredProcedure]
#abcID int = null -- optional param
-- if 0: Group by columnJ, columnK
-- else: Group by columnK, columnJ
, #Grouping int = null
SELECT columnJ, columnK, Count(eID) AS Num, Sum(OutXYZ) as TotalProdXYZ, Sum(RawXYZ) as TotalRawXYZ
FROM [v_ViewTable]
WHERE (#abcID IS NULL OR (abcID = #abcID))
GROUP BY
CASE WHEN #Grouping = 0 THEN columnJ ELSE columnK END
,CASE WHEN #Grouping = 0 THEN columnK ELSE columnJ END
What's wrong with my CASE statement? Basically, if #Grouping = 0, I want the group by to be J, K if anything else, it should be K, J
Thanks in advance for any help!

As the message says columnJ and columnK are not contained in either an aggregate function or the GROUP BY clause in the second case. You should use field or the exact expression in select list AS IS in the GROUP BY section.
So the following statement will be ok:
SELECT CASE WHEN #Grouping = 0 THEN columnJ ELSE columnK END,
CASE WHEN #Grouping = 0 THEN columnK ELSE columnJ END,
Count(eID) AS Num, Sum(OutXYZ) as TotalProdXYZ, Sum(RawXYZ) as TotalRawXYZ
FROM [v_ViewTable]
WHERE (#abcID IS NULL OR (abcID = #abcID))
GROUP BY
CASE WHEN #Grouping = 0 THEN columnJ ELSE columnK END
,CASE WHEN #Grouping = 0 THEN columnK ELSE columnJ END

Related

Optimize multiple case expressions with THEN/END

I have following query.
SELECT
i.id,
CASE WHEN ia.detail_count = 1 THEN i.space_id ELSE NULL END AS space_id,
CASE WHEN ia.detail_count = 1 THEN i.resident_id ELSE NULL END AS resident_id,
CASE WHEN ia.detail_count = 1 THEN i.lease_id ELSE NULL END AS lease_id,
i.deleted_by,
i.deleted_on,
i.updated_by,
i.updated_on,
i.created_by
From
myTable i
JOIN (
SELECT
icd.id,
json_build_object(
'lease_ids', array_remove(array_agg(icd.lease_id), NULL),
'resident_ids', array_remove(array_agg(icd.resident_id), NULL),
'space_ids', array_remove(array_agg(icd.space_id), NULL)
) AS details,
COUNT(icd.id) As detail_count
FROM
mytable_details icd
GROUP BY
icd.id
) ia ON ia.id = i.id;
Can we optimize following three expressions into 1, since condition is same only operand is different.
CASE WHEN ia.detail_count = 1 THEN i.space_id ELSE NULL END AS space_id,
CASE WHEN ia.detail_count = 1 THEN i.resident_id ELSE NULL END AS resident_id,
CASE WHEN ia.detail_count = 1 THEN i.lease_id ELSE NULL END AS lease_id,
I'm not sure that this counts as an optimisation, but I think you could modify the inner query:
(COUNT(icd.id) = 1) as one_detail
... which would return a Boolean result, and then ...
case when one_detail then i.space_id end as space_id,
case when one_detail then i.resident_id end as resident_id,
case when one_detail then i.lease_id end as lease_id,
I'm not sure it's worthwhile in a simple case like this, but for a more complex condition it might be.

I have calculated dimension for my parameter where equals - All. however, I also want it to return null as 0

IIF(
[Parameters].[Year] = 'All'
, 1=1
, [Parameters].[Year] = STR(YEAR([Date]))
)
tableau if parameters return no match than return 0
Try this, will return 1 when finds a value, 0 when it doesn't
INT([Parameters].[Year] = 'All' OR [Parameters].[Year] = STR(YEAR([Date])))

Assign range to variable using between

I have the following decitiontree:
declare #placeholder varchar(20)
If #Number1 = 1
AND #Number2 = 0
BEGIN SET #placeholder = 'NULL'
END
ELSE IF #Number1 = 1
AND #Number2 > 0
BEGIN SET #placeholder = Between (#Number2*10) AND (#Number2*10+9)
END
ELSE
BEGIN
SET #placeholder = #Othervariable
END
I need the Variable for the query:
SELECT * FROM Table
WHERE #Placeholder is null or ID = #placeholder.
But the 'Between' part is not working. Can anyone help me with it?
This won't work in SQL server - there is no variable type that holds something like lambda expression, say Between (#Number2*10) AND (#Number2*10+9).
One way is to store this in string (say, nvarchar(max)) and execute using exec() or sp_executesql().
The other (usually more optimized) way is to form the main expression to include sub-expressions along with exclusion criteria.
Here is an example:
DECLARE #Number1 int = 1 -- input variable 1
DECLARE #Number2 int = 1 -- input variable 2
DECLARE #excl1 bit = 0 -- exclusion criteria 1 (ec1)
DECLARE #excl2 bit = 0 -- exclusion criteria 2 (ec2)
-- fill excl. crit.
SET #excl1 = CASE WHEN #Number1 = 1 AND #Number2 = 0 THEN 1 ELSE 0 END
SET #excl2 = CASE WHEN #Number1 = 1 AND #Number2 > 0 THEN 1 ELSE 0 END
-- just output to see what's happening
PRINT #Number1
PRINT #Number2
PRINT #excl1
PRINT #excl2
SELECT *
FROM Table
WHERE
-- if ec1 is active, we apply sub-expression 1
(#excl1 = 0 OR
(#excl1 = 1 AND ID IS NULL))
AND
-- if ec2 is active, we apply sub-expression 2
(#excl2 = 0 OR
(#excl2 = 1 AND ID BETWEEN #Number2 * 10 AND #Number2 * 10 + 9))

T-SQL 'AND' keyword not short-circuiting it seems

The below stored proc works fine except for the fact that when I uncomment the second part of the date check in the 'where' clause it blows up on a date conversion even if the passed in keyword is null or '111'.
I'm open to any suggestions on how to do this dynamic where clause differently.
I appreciate any help.
ALTER PROCEDURE [SurveyEngine].[GetPageOf_CommentsOverviewRowModel]
#sortColumn varchar(50),
#isASC bit,
#keyword varchar(50)
AS
BEGIN
declare #keywordType varchar(4)
set #keywordType = null
if ISDATE(#keyword) = 1
set #keywordType = 'date'
else if ISNUMERIC(#keyword) = 1
set #keywordType = 'int'
select c.CommentBatch BatchID, c.CreatedDate DateReturned, COUNT(c.CommentID) TotalComments
from SurveyEngine.Comment c
where (#keywordType is null)
or (#keywordType = 'date') --and c.CreatedDate = #keyword)
or (#keywordType = 'int' and (CONVERT(varchar(10), c.CommentBatch) like #keyword+'%'))
group by c.CommentBatch, c.CreatedDate
order by case when #sortColumn = 'BatchID' and #isASC = 0 then c.CommentBatch end desc,
case when #sortColumn = 'BatchID' and #isASC = 1 then c.CommentBatch end,
case when #sortColumn = 'DateReturned' and #isASC = 0 then c.CreatedDate end desc,
case when #sortColumn = 'DateReturned' and #isASC = 1 then c.CreatedDate end,
case when #sortColumn = 'TotalComments' and #isASC = 0 then COUNT(c.CommentID) end desc,
case when #sortColumn = 'TotalComments' and #isASC = 1 then COUNT(c.CommentID) end
END
EDIT Sorry, brain cloud. Things need to be initialized differently.
Change the setup to:
declare #keywordType varchar(4)
declare #TargetDate as DateTime = NULL
set #keywordType = null
if ISDATE(#keyword) = 1
begin
set #keywordType = 'date'
set #TargetDate = Cast( #keyword as DateTime )
end
else if ISNUMERIC(#keyword) = 1
set #keywordType = 'int'
Then change:
and c.CreatedDate = #keyword
to:
and c.CreatedDate = Coalesce( #TargetDate, c.CreatedDate )
That will result in a NOP if you are not searching by date.
Based on this guy's blog: http://blogs.msdn.com/b/bartd/archive/2011/03/03/don-t-depend-on-expression-short-circuiting-in-t-sql-not-even-with-case.aspx it looks like you can't guarantee the order of operations in the where clause, even though short circuiting is supported. The execution plan may choose to evaluate the second statement first.
He recommends using a case structure instead (like pst mentioned before) as it is "more" gauranteed. But I don't think I can rewrite your where clause as a case because you're using three different operators (is null, =, and LIKE).

How do I use T-SQL's Case/When?

I have a huge query which uses case/when often. Now I have this SQL here, which does not work.
(select case when xyz.something = 1
then
'SOMETEXT'
else
(select case when xyz.somethingelse = 1)
then
'SOMEOTHERTEXT'
end)
(select case when xyz.somethingelseagain = 2)
then
'SOMEOTHERTEXTGOESHERE'
end)
end) [ColumnName],
Whats causing trouble is xyz.somethingelseagain = 2, it says it could not bind that expression. xyz is some alias for a table which is joined further down in the query. Whats wrong here? Removing one of the 2 case/whens corrects that, but I need both of them, probably even more cases.
SELECT
CASE
WHEN xyz.something = 1 THEN 'SOMETEXT'
WHEN xyz.somethingelse = 1 THEN 'SOMEOTHERTEXT'
WHEN xyz.somethingelseagain = 2 THEN 'SOMEOTHERTEXTGOESHERE'
ELSE 'SOMETHING UNKNOWN'
END AS ColumnName;
As soon as a WHEN statement is true the break is implicit.
You will have to concider which WHEN Expression is the most likely to happen. If you put that WHEN at the end of a long list of WHEN statements, your sql is likely to be slower. So put it up front as the first.
More information here: break in case statement in T-SQL
declare #n int = 7,
#m int = 3;
select
case
when #n = 1 then
'SOMETEXT'
else
case
when #m = 1 then
'SOMEOTHERTEXT'
when #m = 2 then
'SOMEOTHERTEXTGOESHERE'
end
end as col1
-- n=1 => returns SOMETEXT regardless of #m
-- n=2 and m=1 => returns SOMEOTHERTEXT
-- n=2 and m=2 => returns SOMEOTHERTEXTGOESHERE
-- n=2 and m>2 => returns null (no else defined for inner case)
If logical test is against a single column then you could use something like
USE AdventureWorks2012;
GO
SELECT ProductNumber, Category =
CASE ProductLine
WHEN 'R' THEN 'Road'
WHEN 'M' THEN 'Mountain'
WHEN 'T' THEN 'Touring'
WHEN 'S' THEN 'Other sale items'
ELSE 'Not for sale'
END,
Name
FROM Production.Product
ORDER BY ProductNumber;
GO
More information - https://learn.microsoft.com/en-us/sql/t-sql/language-elements/case-transact-sql?view=sql-server-2017