SQL and ALL operator - sql

Looking for an elegant way to workaround this...
DECLARE #ZIP INT
SET #ZIP = 55555
IF #ZIP = ALL(SELECT ZIP FROM PEOPLE WHERE PERSONTYPE = 1)
PRINT 'All people of type 1 have the same zip!'
ELSE
PRINT 'Not All people of type 1 have the same zip!'
The issue is that, if (SELECT ZIP FROM PEOPLE WHERE PERSONTYPE = 1) returns no records, then the above IF evaluates to true. I'm looking for a way to make this evaluate to false when there are no records returned by the ALL's subquery.
My current solution:
DECLARE #ZIP INT
SET #ZIP = 55555
DECLARE #ALLZIPS TABLE (INT ZIP)
INSERT INTO #ALLZIPS
SELECT ZIP FROM PEOPLE WHERE PERSONTYPE = 1
IF EXISTS(SELECT TOP 1 * FROM #ALLZIPS) AND (#ZIP = ALL (SELECT ZIP FROM #ALLZIPS))
PRINT 'All people of type 1 have the same zip!'
ELSE
PRINT 'Not All people of type 1 have the same zip!'

Use:
IF EXISTS(SELECT NULL
FROM PEOPLE p
WHERE p.persontype = 1
HAVING MIN(p.zip) = #Zip
AND MAX(p.zip) = #Zip)
PRINT 'All people of type 1 have the same zip!'
ELSE
PRINT 'Not All people of type 1 have the same zip!'

Jumping in:
IF (SELECT SUM(CASE WHEN ZIP = #ZIP THEN 0 ELSE 1 END)
FROM PEOPLE WHERE PERSONTYPE = 1) = 0
PRINT 'All people of type 1 have the same zip!'
ELSE
PRINT 'Not All people of type 1 have the same zip!'

Consider using EXISTS as well.
IF #ZIP = ALL(SELECT ZIP FROM PEOPLE WHERE PERSONTYPE = 1)
AND EXISTS(SELECT 1 FROM PEOPLE WHERE PERSONTYPE = 1)

Related

Write Condition as a value for the where clause

I have a variable called _gender thats going to be either 0, 1 or 2
0 male
1 female
2 everyone
I want to achieve the case where _gender is everyone, then I want the gender to be 0 or 1
But if it was 0 or 1, then I should match with users that are only as the variable
SELECT * FROM TABLE
WHERE COUNTRY = 1
AND Gender = --"IF _gender == 2, then I need to get gender either = 0 or 1, else I get the value of the variable (to match 0 or 1)"
You have to Denote Variable starting with #.
Lets consider variable #gender
If you want when #gender is everyone, then the gender to be 0 or 1.
select * from TABLE where Country=1
and Gender = (case when #gender=2 then 0 else #gender end)
If you want when #gender is everyone, then the all type of gender from your table.
select * from TABLE where Country=1
and Gender = (case when #gender=2 then Gender else #gender end)
For reference click below link
Code Reference Link Click Here
I think you are looking for something like:
where country = 1 and
(gender = #gender or #gender = 2)
Often null is used for the value to mean everyone. In that case, you need to phrase it as:
where country = 1 and
(gender = #gender or #gender is null)

SQL Server - aggregate if only one distinct value + nulls without ansi warnings

Suppose I have a data like this
first_name last_name city
John Bon Jovi null
John Lennon null
John Deer null
And I want to create aggregating query which will return json which looks like this
{ "first_name": "John", "city": null }
Essentially, the query should check if there's only one distinct value within each column and if it is, put this value to json. All non-null columns are relatively easy to get with a query like this:
select
case when count(distinct first_name) = 1 then max(first_name) end as first_name,
case when count(distinct last_name) = 1 then max(last_name) end as last_name,
case when count(distinct city) = 1 then max(city) end as city
from ...
for json path, without_array_wrapper
or
select
case when max(first_name) = min(first_name) then max(first_name) end as first_name,
case when max(last_name) = min(last_name) then max(last_name) end as last_name,
case when max(city) = min(city) then max(city) end as city
from ...
for json path, without_array_wrapper
The result of the queries above is json like this {"first_name":"John"}. But then there are problems with nulls. Problem (1) - queries above do not take nulls into account, so if I have data like this
first_name last_name city
----------------------------------
John Lennon null
John Lennon null
John null null
Then last name is also included in the resulting json
{ "first_name": "John", "last_name": "Lennon" }
Ok, that's understandable (cause ...Null value is eliminated by an aggregate...) and I can solve it with a query like this:
select
case when count(distinct first_name) = 1 and count(first_name) = count(*) then max(first_name) end as first_name,
case when count(distinct last_name) = 1 and count(last_name) = count(*) then max(last_name) end as last_name,
case when count(distinct city) = 1 and count(city) = count(*) then max(city) end as city
from ...
for json path, without_array_wrapper
But there are other problems with nulls I can't really solve neatly for now. Problem (2) - I want to have also "city":null in my json. Of course I can do something like this
...
case when count(city) = 0 then 'null' end as city
...
and then replace string null with real nulls, but it's not very neat. Another annoying thing is (3) - I'd really like to get rid of warnings
Warning: Null value is eliminated by an aggregate or other SET operation.
without turning ANSI_WARNINGS off. For now I can only think about using some placeholders with isnull which doesn't look like a clean solution
...
case when count(distinct isnull(city, 'null')) = 1 then max(city) end as city
...
So, any ideas on how to elegantly solve problems (2) and (3)? see examples in db<>fiddle.
Ok, so nobody posted any answers so far, I have thought of one way doing it. It's not perfect, but it seems to work.
So the idea is to use #var = #var + 1 trick inside of select. But it should be a bit more complicated:
declare
#first_name varchar(4), #first_name_state tinyint = 0,
#last_name varchar(4), #last_name_state tinyint = 0,
#city varchar(4), #city_state tinyint = 0,
#country varchar(10), #country_state tinyint = 0,
#result nvarchar(max) = '{}';
select
#first_name_state =
case
when #first_name_state = 0 then 1
when #first_name_state = 1 and #first_name = t.first_name then 1
when #first_name_state = 1 and #first_name is null and t.first_name is null then 1
else 2
end,
#first_name = t.first_name,
#last_name_state =
case
when #last_name_state = 0 then 1
when #last_name_state = 1 and #last_name = t.last_name then 1
when #last_name_state = 1 and #last_name is null and t.last_name is null then 1
else 2
end,
#last_name = t.last_name,
#city_state =
case
when #city_state = 0 then 1
when #city_state = 1 and #city = t.city then 1
when #city_state = 1 and #city is null and t.city is null then 1
else 2
end,
#city = t.city,
#country_state =
case
when #country_state = 0 then 1
when #country_state = 1 and #country = t.country then 1
when #country_state = 1 and #country is null and t.country is null then 1
else 2
end,
#country = t.country
from Table1 as t;
if #first_name_state = 1
set #result = json_modify(json_modify(#result,'$.first_name','null'),'strict $.first_name',#first_name);
if #last_name_state = 1
set #result = json_modify(json_modify(#result,'$.last_name','null'),'strict $.last_name',#last_name);
if #city_state = 1
set #result = json_modify(json_modify(#result,'$.city','null'),'strict $.city',#city);
if #country_state = 1
set #result = json_modify(json_modify(#result,'$.country','null'),'strict $.country',#country);
select #result;
----------------------------------
{"first_name":"John","city":null}
see db<>fiddle with examples.
Please note that, according to Microsoft docs you shouldn't use this variable aggregation assignment trick cause some of the statements can be called more than once.
Don't use a variable in a SELECT statement to concatenate values (that
is, to compute aggregate values). Unexpected query results may occur.
Because, all expressions in the SELECT list (including assignments)
aren't necessarily run exactly once for each output row.
I hope in this case it should work fine cause it's not exactly an aggregation, and it's ok if these statements will be called more than once per row.
Still, you can find some useful links in this answer.

Search Accounts using SQL Server 2008 stored procedure?

I recently started developing/improving the way my search algorithm works. There is few different queries in the system that will use this approach. I'm new in stored procedures world and from what I researched they should improve security, performance and save some code redundancy. Here is example of what I have done in one of my stored procedures for Account search:
ALTER PROCEDURE [dbo].[SearchAccounts]
#Status INT = NULL,
#Type INT = NULL,
#FilterBy INT = NULL,
#Username VARCHAR(50) = NULL,
#Email VARCHAR(80) = NULL,
#LastName VARCHAR(50) = NULL,
#FirstName VARCHAR(50) = NULL,
#FullName VARCHAR(100) = NULL
WITH RECOMPILE
AS
DECLARE #AccountStatus INT = #Status;
DECLARE #AccountType INT = #Type;
DECLARE #AccountFilter INT = #FilterBy;
DECLARE #AccountUsername VARCHAR(50) = #Username;
DECLARE #AccountEmail VARCHAR(80) = #Email;
DECLARE #AccountLast VARCHAR(50) = #LastName;
DECLARE #AccountFirst VARCHAR(50) = #FirstName;
DECLARE #AccountFull VARCHAR(10) = #FullName;
SELECT
A.AccountID, A.FirstName, A.LastName, A.Middle, A.Email,
A.IsUser, A.ActiveUser, A.SystemAdmin, A.AccessType,
A.AccessLevel, A.UserName, A.IsStaff, A.ActiveStaff,
A.Position AS PositionCode, M.Name AS Position
FROM
Accounts AS A
LEFT OUTER JOIN
Master AS M ON M.Tblid = 'STAFF_POS'
AND M.Code = A.Position
WHERE
( -- If Account Type is 1 (User)
(#AccountType = 1 AND A.IsUser = 1)
OR -- If Account Type is 2 (Staff)
(#AccountType = 2 AND A.IsStaff = 1)
OR -- Or if Account type is 0 (All accounts)
(#AccountType = 0 AND 1 = 1)
)
AND
(
(-- If account type is user and Status is 1 (Active) or 0 (Inactive) or 2(pull active and inactive)
#AccountType = 1
AND
(#AccountStatus = 0 OR #AccountStatus = 1) AND ActiveUser = #AccountStatus)
OR
(#AccountType = 1 AND #AccountStatus = 2 AND 1 = 1
)
OR
(-- If account type is staff and Status is 1 (Active) or 0 (Inactive) or 2 (pull active and inactive)
#AccountType = 2
AND
(#AccountStatus = 0 OR #AccountStatus = 1) AND ActiveStaff = #AccountStatus)
OR
(#AccountType = 2 AND #AccountStatus = 2 AND 1 = 1
)
OR
(-- If account type is all pull all accounts active and inactive
(#AccountType != 1 AND #AccountType != 2 AND 1 = 1)
)
)
AND
( -- Filter is 1 then check user name.
(#AccountFilter = 1 AND A.UserName LIKE '%'+#AccountUsername+'%')
OR -- Filter is 2 then check email.
(#AccountFilter = 2 AND A.Email = #AccountEmail)
-- Here if filter is 3 then I should check First or Last or Full Name.
-- Still not sure what is the best approach to filter on the name fields.
)
ORDER BY
A.LastName, A.FirstName
As you can see query above has few different filters. First user can choose if they want to search Users, Staff or pull All account types. Then to choose if they want to pull Active, Inactive or all records. Last thing is to pick the filter. I give them an option to search Username, Email or Name. Each Account has First, Last name. What would be a good option to search for those names? Check just one full name or I have to check first and last separately? First and Last name allow this set of characters in the Accounts form : A-Z, space, dash, apostrophe, period, comma - no other special characters
There are probably dozens of ways to accomplish such a goal. But one way I would prefer (if I was using a query like the one presented) would be like this:
AND A.LNAME LIKE
CASE WHEN Len(#AccountLast) > 0 THEN
'%' + #AccountLast + '%'
ELSE '%'
END
AND A.FNAME LIKE
CASE WHEN Len(#AccountFirst) > 0 THEN
'%' + #AccountFirst + '%'
ELSE '%'
END

SQL CASE wrong output

I have this weird encounter using CASE in sql 2014.
This is my query:
SELECT (CASE WHEN dbo.GetFunctionAge(C.Birthdate) = 0
THEN '' ELSE dbo.GetFunctionAge(C.Birthdate)
END) AS Age
,dbo.GetFunctionAge(C.Birthdate)
,c.Birthdate
FROM Client C
WHERE ClientID = '34d0d845-e3a6-4078-8936-953ff3378eac'
this is the output:
Here is the GetFunctionAge function if you might ask.
IF EXISTS (
SELECT *
FROM dbo.sysobjects
WHERE ID = OBJECT_ID(N'[dbo].[GetFunctionAge]') AND
xtype in (N'FN', N'IF', N'TF'))
DROP FUNCTION [dbo].[GetFunctionAge]
GO
CREATE FUNCTION [dbo].[GetFunctionAge](#BirthDate DATETIME)
RETURNS INT
AS
BEGIN
DECLARE #Age INT
IF(#BirthDate = '1753-01-01 00:00:00.000')
BEGIN
SET #Age = 0
END
ELSE
BEGIN
SET #Age = DATEDIFF(hour,#BirthDate,GETDATE())/8766
END
RETURN #Age
END
GO
Question:
Why is Column Age in my output is 0which should be ''?
I added (No column name) to show that its output is 0 so my expected output base from my case condition is '' not 0
I didn't receive any error regarding inconsistency of data so why is case behaving like that?
Thanks for those who could clarify this to me.
SELECT
(CASE
WHEN a.ageint = 0 THEN ''
ELSE cast(a.ageint as varchar(3))
END) AS Age
, a.ageint
, c.Birthdate
FROM Client as C
CROSS APPLY (
SELECT
ISNULL(dbo.GetFunctionAge(C.Birthdate), 0) AS ageint
) AS a
WHERE ClientID = '34d0d845-e3a6-4078-8936-953ff3378eac'
;
You can cast it into varchar so you can return ' '.
SELECT (CASE WHEN dbo.GetFunctionAge(C.Birthdate) = 0
THEN '' ELSE Cast(dbo.GetFunctionAge(C.Birthdate) as varchar(5))
END) AS Age
,dbo.GetFunctionAge(C.Birthdate)
,c.Birthdate
FROM Client C
WHERE ClientID = '34d0d845-e3a6-4078-8936-953ff3378eac'
But If you wish to remain your Age column in data type int.
You could just use NULL instead of ' '

Stored Procedure - issues with query

I'm simply unable to to find a solution for this stored procedure query.
I have Person and a Student table. In Person table I store a bool value isFootballPlayer and Student table has an FK PersonId that links it to the Person table.
I have a web app where there is a search functionality that includes checkbox for Student, Person, and Football Player, in order to filter the results. So when only Person is selected, it will return all which are not Players nor Students.
My stored procedure looks like this:
"SP stuff"
#IncludePerson bit,
#IncludePlayer bit,
#IncludeStudent bit
AS
BEGIN
WITH i (Id)
SELECT
p.PersonId as Id
FROM
Person p
LEFT OUTER JOIN
Student s ON s.PersonId = p.PersonId
WHERE
(s.PersonId IS NULL OR #IncludeStudent = 1) AND
(p.IsFootballPlayer = 0 OR #IncludePlayer = 1) AND
((s.PersonId > 0) OR #IncludePerson = 1)
)
SELECT i.Id
FROM i
GROUP BY i.Id
END
The issue here is that some of the students can also be football players, and so when only the student checkbox is checked, the result excludes the students that are also football players, unless both checkboxes are checked.
Can anyone help me in the right direction and give me some tip on how I can modify the stored procedure to manage to show students that are also football players, without having to check both checkboxes?
Thanks
"SP stuff"
#IncludePerson bit,
#IncludePlayer bit,
#IncludeStudent bit
AS
BEGIN
Declare #Sql NVARCHAR(MAX);
SET #Sql = N'SELECT DISTINCT p.PersonId as Id
FROM Person p
WHERE 1 = 1 '
-- When only Person checkbox is checked
+ CASE WHEN (#IncludePerson = 1 AND #IncludeStudent = 0 AND #IncludePlayer = 0)
THEN N' AND p.IsFootballPlayer = 0
AND NOT EXISTS (SELECT 1 FROM Student s
WHERE s.PersonId = p.PersonId)' ELSE N'' END
-- When only Student checkbox is checked
+ CASE WHEN (#IncludePerson = 0 AND #IncludeStudent = 1 AND #IncludePlayer = 0)
THEN N' AND EXISTS (SELECT 1 FROM Student s
WHERE s.PersonId = p.PersonId)' ELSE N'' END
-- When only Player checkbox is checked
+ CASE WHEN (#IncludePerson = 0 AND #IncludeStudent = 0 AND #IncludePlayer = 1)
THEN N' AND p.IsFootballPlayer = 1 ' ELSE N'' END
-- And you can add as many as combinations you want
-- Finally execute the dynamically built sql query
Exec sp_executesql #Sql
END
Why don't you set the variable #IncludePlayervalue = 1 inside your stored procedure when #IncludeStudent = 1 like mentioned below -
"SP stuff"
#IncludePerson bit,
#IncludePlayer bit,
#IncludeStudent bit
AS
BEGIN
if #IncludeStudent = 1
begin
set #IncludePlayer = 1
end
WITH i (Id)
SELECT
p.PersonId as Id
FROM
Person p
LEFT OUTER JOIN
Student s ON s.PersonId = p.PersonId
WHERE
(s.PersonId IS NULL OR #IncludeStudent = 1) AND
(p.IsFootballPlayer = 0 OR #IncludePlayer = 1) AND
((s.PersonId > 0) OR #IncludePerson = 1)
)
SELECT i.Id
FROM i
GROUP BY i.Id
END