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
Related
I have this stored procedure for a course registration system I am working on. My intention is to return a value of -1 if the query returns a course which a student has not taken according to the course pre requisites.
The pre req table only has two columns; CourseID being the course and PreReqCourse_ID being the required course for that specified course. If the student has taken all pre req courses then it should return a value of 1. I keep getting a value of -1 even when I run the query for a student who has taken the required pre req courses. Any help would be much appreciated!
CREATE PROCEDURE CheckPreReq
#StudentID INT,
#CourseID INT
AS
DECLARE #theCount INT
IF EXISTS (SELECT *
FROM PreReq
INNER JOIN Student_History ON (PreReq.Course_ID = #CourseID)
WHERE Student_History.Course_ID != PreReq.PreReqCourse_ID
AND Student_History.Student_ID = #StudentID)
BEGIN
SET #theCount =-1
END
ELSE
BEGIN
SET #theCount = 1
END
RETURN #theCount
Would something like this work?
DECLARE #PreReqsTotal tinyint
DECLARE #PreReqsFulfilled tinyint
-- Count pre-req courses.
SELECT #PreReqsTotal = COUNT(*)
FROM PreReq
WHERE [CourseID] = #CourseId
-- Count how many fulfilled by student.
SELECT #PreReqsFulfilled = count(*)
FROM Student_History hist
JOIN PreReq pre
on hist.Course_ID = pre.PreReqCourse_ID
WHERE pre.CourseID = #CourseID
and hist.Student_ID = #StudentID
RETURN CASE WHEN #PreReqsTotal = #PreReqsFulfilled THEN 1 ELSE -1 END
...or something like this:
IF EXISTS
(
SELECT blah.*
FROM
(
SELECT pre.*
,[PreFulfilled] = CASE WHEN hist.Course_ID is null THEN 0 ELSE 1 END
FROM PreReq pre
LEFT JOIN
Student_History hist
on pre.PreReqCourse_ID = hist.Course_ID
and hist.Student_ID = #StudentID
WHERE pre.CourseID = #CourseID
) blah
WHERE blah.[PreFulfilled] = 0 -- Unfulfilled PreReq.
)
BEGIN
RETURN -1 -- Has an unfulfilled PreReq.
END
RETURN 1 -- No unfulfilled PreReqs.
You should JOIN the PreReq table with the Student_History on the the PreReq.PreReqCourse_ID and Student_History.CourseID columns (take a look at my example). Then your SP should work.
--create tmp example tables
IF OBJECT_ID('tempdb..#PreReq') IS NOT NULL DROP TABLE #PreReq
CREATE TABLE #PreReq(
CourseID int,
PreReqCourse_ID int
)
--insert Course 3 which depends on Course 2 and 1 in #PreReq
INSERT INTO #PreReq
values(3,2),(3,1)
IF OBJECT_ID('tempdb..#Student_History') IS NOT NULL DROP TABLE #Student_History
CREATE TABLE #Student_History(
CourseID int not null,
StudentID int not null
);
--insert Student 1 who has visited Course 1 and 2
insert into #Student_History
VALUES(1,1),(2,1)
--declare variables
DECLARE #CourseID AS INT = 3
,#StudentID AS INT = 1
--COUNT on how many Courses #CourseID depends
,#necessaryCourses AS INT
--COUNT on how many Courses the Student has taken
,#countTakenCourses AS INT
,#theCount AS INT
SET #necessaryCourses = (SELECT count(*) FROM #PreReq WHERE CourseID = #CourseID);
SET #countTakenCourses = (
SELECT count(*)
FROM #PreReq p
--JOIN with Student_History to check if the student has visited the necessary course
JOIN #Student_History h on p.PreReqCourse_ID = h.CourseID
WHERE p.CourseID = #CourseID AND h.StudentID = #StudentID
)
IF #necessaryCourses = #countTakenCourses
BEGIN
set #theCount = 1
END
ELSE
BEGIN
set #theCount = -1
END
SELECT #theCount AS theCount
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
I am running a SQL query that may return an empty rowset. I want to handle that with using a CASE statement.
This what I have tried but is not working
Case
When ##ROWCOUNT > 0 Then LTRIM(RTRIM(t.FirstName)) Else 'UNKNOWN'
End
The case statement does not return any value
If I use print ##RowCount rather then Select I get a 0 value.
How can I solve this?
Okay the query is rather large so I'll try to explain what it does. I am using this in a function that returns data from our HR database. I need Employee information such as name and work assignments. There are cases where I need to process the data where the employee is unknown. In those cases I pass an employeeId number of zero. That results in an empty return when I run the query.
What I would like to do is have the function return the Name data as UNKNOWN.
Hope this helps clear up the request a little
I have trimmed up the query so I can show all of the relevant parts below:
DECLARE #EID int = 0
DECLARE #AssignmentType varchar(max) = 'FirstName'
DECLARE #ReturnValue varchar(max)
DECLARE #EIDTemp TABLE(EID varchar(6), Section varchar(max), Division
varchar(max), Bureau varchar(max), FirstName varchar(max), MiddleName
varchar(max), LastName varchar(max));
Insert Into #EIDTemp
SELECT L1.Employee, P1.NAME As Section,
CASE
WHEN L1.DEPARTMENT IN (7010) THEN 'Legal Services Division'
WHEN L1.DEPARTMENT IN (7030,7040) THEN 'Fiscal Management Division'
Else 'Other'
END AS Division,
CASE
WHEN L1.DEPARTMENT IN (7130) THEN 'Administrative Services Bureau'
ELSE ','
END As Bureau
,L1.FIRST_NAME
,L1.MIDDLE_NAME
,L1.LAST_NAME
FROM [SOOPS-LAWREPT].[LAWDATA].dbo.EMPLOYEE L1
JOIN [SOOPS-LAWREPT].[LAWDATA].dbo.DEPTCODE L2 ON L1.Department =
L2.Department
JOIN [SOOPS-LAWREPT].[LAWDATA].dbo.JOBCODE L3 on L1.JOB_CODE = L3.JOB_CODE
JOIN [SOOPS-LAWREPT].[LAWDATA].dbo.PRSYSTEM P1 ON L1.PROCESS_LEVEL =
p1.PROCESS_LEVEL
WHERE L1.EMP_STATUS='A1'
-- Select the Return Value
-------------------------------
IF (#AssignmentType = 'FirstName')
BEGIN
SET #ReturnValue = (SELECT TOP 1 LTRIM(RTRIM(t.MiddleName)) From #EIDTemp t
where EID = #EID);
END
IF (#AssignmentType = 'MiddleName')
BEGIN
SET #ReturnValue = (SELECT TOP 1 LTRIM(RTRIM(t.MiddleName)) From #EIDTemp t
where EID = #EID);
END
IF (#AssignmentType = 'LastName')
BEGIN
SET #ReturnValue = (SELECT TOP 1 LTRIM(RTRIM(t.LastName)) From #EIDTemp t
where EID = #EID);
END
The problem is how you are setting your variables. When you use SET it will change the value to NULL if no rows are returned.
You should instead change up your code so it does something like this.
SET #ReturnValue = 'Unknown'
IF (#AssignmentType = 'FirstName')
BEGIN
SELECT #ReturnValue = LTRIM(RTRIM(t.MiddleName)) From #EIDTemp t
where EID = #EID
END
This sets an initial value to Unknown. This would be before all of your IF statements and would work across the whole collection. If there are no rows returned the value will remain unchanged. I removed the TOP 1 because it is not needed. If you have multiple rows it will receive the value from the last row in the result set. If there are lots of rows you could always use MAX or MIN.
#Perry, is the following more like what you are looking for:
WITH employee_ids AS
(
SELECT
emp.Employee AS eid
,P1.NAME As Section
,CASE
WHEN emp.DEPARTMENT IN (7010) THEN
'Legal Services Division'
WHEN emp.DEPARTMENT IN (7030,7040) THEN
'Fiscal Management Division'
ELSE
'Other'
END AS Division
,CASE
WHEN emp.DEPARTMENT IN (7130) THEN
'Administrative Services Bureau'
ELSE
','
END As Bureau
,emp.FIRST_NAME
,emp.MIDDLE_NAME
,emp.LAST_NAME
,ROW_NUMBER() OVER (PARTITION BY emp.EMPLOYEE) AS eid_match
FROM
[SOOPS-LAWREPT].[LAWDATA].dbo.EMPLOYEE AS emp
INNER JOIN
[SOOPS-LAWREPT].[LAWDATA].dbo.DEPTCODE AS depts
ON (emp.Department = depts.Department)
INNER JOIN
[SOOPS-LAWREPT].[LAWDATA].dbo.JOBCODE AS jcs
ON (emp.JOB_CODE = jcs.JOB_CODE)
INNER JOIN
[SOOPS-LAWREPT].[LAWDATA].dbo.PRSYSTEM AS prs
ON (emp.PROCESS_LEVEL = prs.PROCESS_LEVEL)
WHERE
emp.EMP_STATUS='A1'
)
,provisional_results AS
(
SELECT
CASE #AssignmentType
WHEN 'FirstName' THEN
employee_ids.FirstName
WHEN 'MiddleName' THEN
employee_ids.MiddleName
WHEN 'LastName' THEN
employee_ids.LastName
ELSE
NULL
END AS provisional_return_value
FROM
(VALUES (0)) AS default_values(default_value)
LEFT JOIN
employee_ids
ON (employee_ids.eid = #eid)
AND (eid_match = 1)
)
SELECT
#ReturnValue = ISNULL(LTRIM(RTRIM(provisional_return_value)), 'UNKNOWN')
FROM
provisional_results
I haven't tested the code as I'm not at my desktop so excuse any small syntax errors, but it should achieve what you need.
You need to save the ##RowCount variable immediately after running the insert/select and return the variable. If you don't save the variable, another statement will eventually clear the rowcount...
Example:
--Variable to hold the rowcount
DECLARE #Count int = 0
...
Insert Into #EIDTemp
SELECT L1.Employee, P1.NAME As Section,
...
WHERE L1.EMP_STATUS='A1'
--Save the result
SELECT #Count=##RowCount
...
--Use the saved result
Case
When #Count > 0 Then LTRIM(RTRIM(t.FirstName)) Else 'UNKNOWN'
End
A co-worker of mine solved the problem by checking the value of the Employee ID at the start and returning UNKNOWN with this code
> DECLARE #EID int = 0
DECLARE #AssignmentType varchar(max) = 'FirstName'
DECLARE #ReturnValue varchar(max) = 'UNKNOWN'
DECLARE #EIDTemp TABLE(EID varchar(6), Section varchar(max), Division
varchar(max), Bureau varchar(max), FirstName varchar(max), MiddleName
varchar(max), LastName varchar(max));
if #EID = 0
goto done
-- Insert into a Temp Table
-------------------------------
Insert Into #EIDTemp
SELECT L1.Employee, P1.NAME As Section,
CASE
WHEN L1.DEPARTMENT IN (7010) THEN 'Legal Services Division'
WHEN L1.DEPARTMENT IN (7030,7040) THEN 'Fiscal Management
Division'
Else 'Other'
END AS Division,
CASE
WHEN L1.DEPARTMENT IN (7130) THEN 'Administrative Services Bureau'
ELSE ','
END As Bureau
,L1.FIRST_NAME
,L1.MIDDLE_NAME
,L1.LAST_NAME
FROM [SOOPS-LAWREPT].[LAWDATA].dbo.EMPLOYEE L1
JOIN [SOOPS-LAWREPT].[LAWDATA].dbo.DEPTCODE L2 ON L1.Department =
L2.Department
JOIN [SOOPS-LAWREPT].[LAWDATA].dbo.JOBCODE L3 on L1.JOB_CODE =
L3.JOB_CODE
JOIN [SOOPS-LAWREPT].[LAWDATA].dbo.PRSYSTEM P1 ON L1.PROCESS_LEVEL =
p1.PROCESS_LEVEL
WHERE L1.EMP_STATUS='A1' AND l1.EMPLOYEE = #EID
-- Select the Return Value
-------------------------------
IF (#AssignmentType = 'FirstName')
BEGIN
SET #ReturnValue = (SELECT TOP 1 LTRIM(RTRIM(t.FirstName)) From #EIDTemp
t where EID = #EID);
END
IF (#AssignmentType = 'MiddleName')
BEGIN
SET #ReturnValue = (SELECT TOP 1 LTRIM(RTRIM(t.MiddleName)) From
#EIDTemp t where EID = #EID);
END
IF (#AssignmentType = 'LastName')
BEGIN
SET #ReturnValue = (SELECT TOP 1 LTRIM(RTRIM(t.LastName)) From #EIDTemp
t where EID = #EID);
END
done:
print #ReturnValue
I have a part of a trigger like so -
DECLARE #isInsert TINYINT
SET #isInsert = (CASE #actionType WHEN 'I' THEN 1 ELSE 0 END)
SELECT
(CASE #isInsert WHEN 1 THEN i.groupId ELSE d.groupId END) AS groupId
INTO #tmpRecordPermissionsToCheck
FROM inserted i
FULL JOIN deleted d
ON i.userId = d.userId
AND
i.groupId = d.groupId
-- Stop everything if the user is attempting to edit something they're not entitled to...
-- special case(s): refer above for additional tblServer-specific checks required here
DECLARE #errMsg VARCHAR(255)
SELECT #errMsg = 'You do not have permission to edit permissions for group ' + IsNULL(ug.shortName, '')
FROM #tmpRecordPermissionsToCheck tmp
LEFT JOIN tblUserGroups ug
ON ug.groupId = tmp.groupId
WHERE dbo.hasAdministrativePermissionsForGroup(tmp.groupId, dbo.getCurrentUser()) = 0
IF (#errMsg IS NOT NULL)
BEGIN
RAISERROR ( #errMsg, 16, 1 )
ROLLBACK TRANSACTION
RETURN
END
I'm calling a separate function that returns a 0 or 1 bit value.
If I do select dbo.isGlobalAdministrator(dbo.getCurrentUser()) I get a 1.
How do I structure the above code so that the IF (#errMsg IS NOT NULL) can be overridden if dbo.isGlobalAdministrator(dbo.getCurrentUser()) = 1 ?
How do I structure the above code so that the IF (#errMsg IS NOT NULL) can be overridden if dbo.isGlobalAdministrator(dbo.getCurrentUser()) = 1 ?
When you say overridden,i think you want to bypass errormessage
so just add this above error message
if ( dbo.isGlobalAdministrator(dbo.getCurrentUser()) = 1)
return
I want to insert in sql query something like that:
Select * from Users where id=[if #userId>3 then #userId else "donnt use this condition"] and Name=[switch #userId
case 1:"Alex"
case 2:"John"
default:"donnt use this condition"];
How can i do it?
yet another similar question
When showAll is false it works ok but when showAll is true it returns nothing. Why and how to make it working right? IsClosed column has a bit type.
Select * from orders where IsClosed=CASE WHEN #showAll='false' THEN 'false' ELSE NULL END;
This will perform horribly:
Select *
from Users
where (#userid > 3 AND id = #userId)
OR (#userId BETWEEN 1 AND 2 AND name = CASE
WHEN #userId = 1 THEN 'Alex'
ELSE 'John'
END)
The best performing option is dynamic SQL:
SQL Server 2005+
DECLARE #SQL NVARCHAR(4000)
SET #SQL = 'SELECT u.*
FROM USERS u
WHERE 1 = 1 '
SET#SQL = #SQL + CASE
WHEN #userId > 3 THEN ' AND u.id = #userId '
ELSE ''
END
SET#SQL = #SQL + CASE #userId
WHEN 1 THEN ' AND u.name = ''Alex'' '
WHEN 2 THEN ' AND u.name = ''John'' '
ELSE ''
END
BEGIN
EXEC sp_executesql #SQL, N'#userId INT', #userId
END
For more info on SQL Server's dynamic SQL support, read "The Curse and Blessings of Dynamic SQL"
Select *
from Users
where id = CASE WHEN #userId>3 THEN #userId ELSE NULL END
OR name = CASE WHEN #userId = 1 THEN 'Alex' WHEN #UserId = 2 THEN 'John' ELSE NULL END
Please try this:
select *
from Users
where id = (case when #userId > 3 then #userId
else id end)
and Name = (case cast(#userId as varchar)
when '1' then 'Alex'
when '2' then 'John'
else Name end)
Or I think this will perform better:
select aa.*
from (select *
, case when #userId > 3 then #userId
else id end as UserID
, case cast(#userId as varchar)
when '1' then 'Alex'
when '2' then 'John'
else Name end as UserName
from Users) aa
where aa.id = aa.UserID
and aa.Name = aa.UserName
You might want to define each field that you only need on your select, instead of using asterisk(*).