Join on Table Variable if it has data - sql-server

I have a stored procedure that accepts a user defined table type, which is just a list of ints.
If the table is not set (Either sent in as NULL, or has no rows), then my existing query to return data is OK. But I need to only return Ids that are in that table variable.
So if the table variable has data, I would like to INNER JOIN on it, to only return matching Ids.
So that the moment, it's a basic query like this: (Example)
SELECT ...
FROM MyTable
Where UserId = 1
But I need to somehow:
SELECT ...
FROM MyTable m
INNER JOIN #MyTableVariable v ON v.Id = m.Id, --But only if #MyTableVariable has data
Where UserId = 1
Can I do the inner join, only when there's data in #MyTableVriable? Or maybe EXITSTS would help?

Of course, a relatively simple approach (assuming your SELECT statement is not too complicated and you don't mind "duplicating" it) would be:
IF EXISTS (SELECT 1 FROM #MyTableVariable)
BEGIN
SELECT ...
FROM MyTable m
INNER JOIN #MyTableVariable v ON v.Id = m.Id, --But only if #MyTableVariable has data
Where UserId = 1;
END
ELSE
BEGIN
SELECT ...
FROM MyTable m
Where UserId = 1;
END
Otherwise
SELECT ...
FROM MyTable m
LEFT OUTER JOIN #MyTableVariable v ON v.Id = m.Id, --But only if #MyTableVariable has data
Where UserId = 1
AND
(
(EXISTS (SELECT 1 FROM #MyTableVariable)
AND v.Id Is Not NULL)
OR
(NOT EXISTS (SELECT 1 FROM #MyTableVariable))
);
I'll stand corrected if I'm wrong (just don't have the opportunity to test it myself right now), but I believe that even if NULL was passed in as the #MyTableVariable value, SQL would still see it as an empty table (with the corresponding structure of the UDT)

SQL Server provides conditional statemets; then, you can try this approach bellow, with a conditional select, based on your table variable status (in this example, checking if is not empty):
IF(EXISTS(SELECT 1 FROM #MyTableVariable))
BEGIN
SELECT ...
FROM MyTable m
INNER JOIN #MyTableVariable v ON v.Id = m.Id
WHERE UserId = 1
END
ELSE
BEGIN
SELECT ...
FROM MyTable
WHERE UserId = 1
END

Related

SQL Server - Get record where not exist on other table

I have two tables:
I want to get result in Campaign table - > 55,100.
Because they don't exist in Jobs table.
You can use a not exists condition:
SELECT *
FROM campaign c
WHERE NOT EXISTS (SELECT *
FROM jobs j
WHERE j.campaignid = c.campaignid)
you can use like this:
select * from campaign
where 1 = 1
and campaignid is not null
and campaignid not in (select campaignid from jobs)
create table #Campaign (CampaignId int NULL)
create table #Jobs (JobId int, CampaignId int)
insert into #Campaign values (1),(2),(3),(4),(100),(55)
insert into #Jobs values (1,1),(2,2),(3,3),(4,4),(5,1),(6,3),(7,3)
select distinct C.CampaignId
from #Campaign as C
where not exists(select 1 from #Jobs as J where C.CampaignId = J.CampaignId)
Result is
55
100
Or you can use a LEFT OUTER JOIN where the id is null:
SELECT c.* from campaign c
LEFT OUTER JOIN jobs j ON j.CampaignId = c.CampaignId
WHERE j.CampaignId IS NULL;

MSSQL: reusable way of querying subset of all data in a table

Suppose a system where People have access to Buildings and in the buildings, have access to certain Rooms.
Access is defined according to the associated Permissions tables or in the case of people with full access, full access.
The db tables are defined as follows:
buildings (id INT)
rooms (id INT, building_id INT)
people (id INT, has_full_access BIT)
building_permissions (building_id INT, person_id INT)
room_permissions (room_id INT, person_id INT)
Currently I have table-valued functions that return tables with the authorised ids of buildings and rooms based on the person's id and whether they have full access.
CREATE FUNCTION fn_get_authorised_buildings (#person_id INT, #has_full_access BIT)
RETURNS TABLE AS
RETURN
(
SELECT b.id
FROM buildings b
WHERE #has_full_access = 1
UNION
SELECT b.id
FROM buildings b
INNER JOIN building_permissions bp ON bp.building_id = b.id
WHERE bp.person_id = #person_id
);
CREATE FUNCTION fn_get_authorised_rooms (#person_id INT, #has_full_access BIT)
RETURNS TABLE AS
RETURN
(
SELECT r.id
FROM rooms r
WHERE #has_full_access = 1
UNION
SELECT r.id
FROM rooms r
INNER JOIN room_permissions rp ON rp.room_id = r.id
WHERE rp.person_id = #person_id
);
Throughout the system whenever buildings and rooms are involved I need to filter those tables based on the person's permissions. If the person has full access then all the rows must be included, otherwise only those that they have permission for.
My queries (simplified) look something like this:
DECLARE #person_id INT = 123
DECLARE #has_full_access BIT = 1
DECLARE #authorised_buildings TABLE (id INT)
INSERT INTO #authorised_buildings SELECT id FROM fn_get_authorised_buildings(#person_id , #has_full_access)
DECLARE #authorised_rooms TABLE (id INT)
INSERT INTO #authorised_rooms SELECT id FROM fn_get_authorised_rooms(#person_id, #has_full_access)
--Example A
SELECT *
FROM buildings b
INNER JOIN rooms r ON r.building_id = b.id
WHERE 1 = 1
AND b.id IN (SELECT id FROM #authorised_buildings)
AND r.id IN (SELECT id FROM #authorised_rooms)
--Example B
SELECT *
FROM floors f -- or other tables that are related to rooms
INNER JOIN rooms r ON r.floor_id = f.id
WHERE 1 = 1
AND r.id IN (SELECT id FROM #authorised_rooms)
Is there a better way to do this?
EDIT:
Here is a fiddle with the setup
You can create a view like this that contains all the logic:
CREATE VIEW bulding_rooms_per_person AS
SELECT p.id AS person_id, b.id as building_id, r.id AS room_id
FROM people p
LEFT JOIN building_permissions bp ON bp.person_id = p.id
LEFT JOIN room_permissions rp ON rp.person_id = p.id
INNER JOIN buildings b ON b.id = bp.building_id OR p.has_full_access = 1
INNER JOIN rooms r ON r.building_id = b.id AND (r.id = rp.room_id OR p.has_full_access = 1)
And then just query over it:
SELECT * from bulding_rooms_per_person WHERE person_id = #person_id

Create temp table and load data into it using Stored Procedure

I have three different SQL scripts, and I am able to execute them and get the desired results. I want to put them all into a Temp Table, and use it as a Stored procedure, and get the data displayed on the temp table. Below is the Procedure that I have created.
I want to know how to create a temp table and insert this data into it by executing the SP.
CREATE PROCEDURE [dbo].[sp_SurveyTracking]
AS
BEGIN
SET NOCOUNT ON;
-- AVAILABLE
SELECT DISTINCT P.Clinic
, P.LastName
, P.FirstName
, SA.SI AS SID
, ser.SD
-- , 'Available' as Status
, ss.SDate
, ss.SDtTm
FROM
Table_Name SA
Inner join Schedule ss on sa.SIID = ss.SIID
Inner join Patient p on p.PID = ss.PID
Inner join Clinical.SSJun ssj on ssj.SSJID = ss.SSJID
Inner join Clinical.SS ser on ser.SSID = ssj.SSID
WHERE
SS.SDtTm IS NOT NULL
AND (SS.SDtTm < SS.SDate)
AND (SS.SDate > GETDATE())
AND SS.Canc = 0
AND sa.AD = 'Completed'
AND GETDATE() > ss.SDate
Order by p.CID;
--COMPLETED
SELECT DISTINCT p.Clinic
, p.LastName
, p.FirstName
, ser.SD
--, 'Completed' as Status
, ss.SDate
FROM
Table_Name SA
Inner join Schedule ss on sa.SIID = ss.SIID
Inner join Patient p on p.PID = ss.PID
Inner join Clinical.SSJun ssj on ssj.SSJID = ss.SSJID
Inner join Clinical.SS ser on ser.SSID = ssj.SSID
WHERE
sa.AD = 'Completed' and ss.Cancelled = 0
order by p.CID;
-- OVERDUE
SELECT DISTINCT p.CD
,p.LastName
,p.FirstName
,sa.SIID
,ser.SD
,'Overdue' as Status
,ss.SDate
,GetDate() as CurrentDate
FROM
Table_Name SA
Inner join Schedule ss on sa.SIID = ss.SIID
Inner join Patient p on p.PID = ss.PID
Inner join Clinical.SSJun ssj on ssj.SSJID = ss.SSJID
Inner join Clinical.SS ser on ser.SSID = ssj.SSID
WHERE
GetDate() > ss.SDate and ss.Cancelled = 0
order by p.CID;
END
You can define the #table in the beginning and then use the
INSERT INTO
to add the values into it.
Something like:
CREATE TABLE #tmp
(
SomeFields VARCHAR(MAX)
);
INSERT INTO #tmp
SELECT * FROM table2
Please check the tick button if you like the answer. Thanks.
To insert data from Storec procedure into a table / temp table , first you need to create the table definition
Given below is the sample you can do.
create proc dbo.test
as
select 1 as b
create table #tmp(b int)
insert into #tmp exec dbo.test

T-SQL insert static values into resultant set from another joined query

I need to insert 2 values into a table based on other tables' select results.
IF NOT EXISTS
(
SELECT M.DNUM, M.NAME, U.ID, A.ID, A.RIGHT
FROM [ACCESS] A JOIN [MASTER] M
ON M.DNUM = A.NUM
JOIN [USERS] U
ON U.NUM = D.ID
WHERE M.ALIAS = '0-50'
GROUP BY M.DNUM, M.NAME, U.ID, A.ID, A.RIGHT
)
BEGIN
INSERT INTO [ACCESS]
(ID, RIGHT)
VALUES
('9','3')
END
I need to add the 2 values to the [ACCESS] table but only want to add the values if M.ALIAS is 0-50 AND if A.NUM = M.NUM.
When I ran the script it completed without error but insert did not happen when I recheck.
Thank you for your help.
Add ELSE clause with PRINT to test IF condition

Incorrect syntax near the keyword 'IF'

Why do I get "Incorrect syntax near the keyword 'IF'" in the following SQL?:
use AdventureWorks
CREATE FUNCTION Query2_Function(#DPT INT)
RETURNS TABLE
AS
RETURN
IF #DPT is not null
select edh.departmentid,d.name,count(*)as cnt
From HumanResources.Employee e
inner join HumanResources.EmployeeDepartmentHistory edh on e.employeeID = edh.employeeid
inner join humanresources.department d on edh.departmentid = d.departmentid
where d.Name = #dpt
group by edh.departmentid, d.name
You cannot have any flow of control statements in inline table valued functions. If #dpt is null the query will return an empty result set anyway
Edit: or at least would if the correct datatype. You have #DPT INT and are comparing against a name column. That seems doomed to failure at execution time.
Edit 2:
As a solution, you could 1) simply drop the IF #DPT is not null line and 2) either
change the #DPT parameter's type from INT to something like varchar(100), if the function was supposed to search for the department by name,
or
change the WHERE clause to something like this:
where d.departmentid = #dpt
if you meant it to search by department ID.
Try this
CREATE FUNCTION Query2_Function(#DPT INT)
RETURNS #tbl TABLE
(
departmentid int ,
[name] varchar(100),
cnt int
)
AS
begin
IF #DPT is not null
insert into #tbl (departmentid,name,cnt)
select edh.departmentid,d.name,count(*)as cnt
From HumanResources.Employee e
inner join HumanResources.EmployeeDepartmentHistory edh on e.employeeID = edh.employeeid
inner join humanresources.department d on edh.departmentid = d.departmentid
where d.DepartmentID =#DPT
group by edh.departmentid, d.name
return
end
GO

Resources