Avoid referring table two times in the WHERE clause - sql-server

Following is a simplified version of my database in SQL Server 2005. I need to select employees based on business units. Each employee has home department, parent department and visiting department. Based on the department, business unit can be found out.
For an employee, if the HomeDeptID = ParentDeptID, then
#SearchBusinessUnitCD should be present for the VisitingDeptID.
If HomeDeptID <> ParentDeptID, then #SearchBusinessUnitCD should be
present for the ParentDeptID.
Following query works fine. But it has scan on the #DepartmentBusinesses table two times. Is there a way to use the table #DepartmentBusinesses only once by making it as a CASE statement or similar?
DECLARE #SearchBusinessUnitCD CHAR(3)
SET #SearchBusinessUnitCD = 'B'
--IF HomeDeptID = ParentDeptID, then #SearchBusinessUnitCD should be present for the VisitingDeptID
--IF HomeDeptID <> ParentDeptID, then #SearchBusinessUnitCD should be present for the ParentDeptID
CREATE TABLE #DepartmentBusinesses (DeptID INT, BusinessUnitCD CHAR(3))
INSERT INTO #DepartmentBusinesses
SELECT 1, 'A' UNION ALL
SELECT 2, 'B'
CREATE NONCLUSTERED INDEX IX_DepartmentBusinesses_DeptIDBusinessUnitCD ON #DepartmentBusinesses (DeptID,BusinessUnitCD)
DECLARE #Employees TABLE (EmpID INT, HomeDeptID INT, ParentDeptID INT, VisitingDeptID INT)
INSERT INTO #Employees
SELECT 1, 1, 1, 2 UNION ALL
SELECT 2, 2, 1, 3
SELECT *
FROM #Employees
WHERE
(
HomeDeptID = ParentDeptID
AND
EXISTS (
SELECT 1
FROM #DepartmentBusinesses
WHERE DeptID = VisitingDeptID
AND BusinessUnitCD = #SearchBusinessUnitCD)
)
OR
(
HomeDeptID <> ParentDeptID
AND
EXISTS (
SELECT 1
FROM #DepartmentBusinesses
WHERE DeptID = ParentDeptID
AND BusinessUnitCD = #SearchBusinessUnitCD
)
)
DROP TABLE #DepartmentBusinesses
Plan

SELECT *
FROM #Employees e
WHERE EXISTS (
SELECT 1
FROM #DepartmentBusinesses t
WHERE t.BusinessUnitCD = #SearchBusinessUnitCD
AND (
(e.HomeDeptID = e.ParentDeptID AND t.DeptID = e.VisitingDeptID)
OR
(e.HomeDeptID != e.ParentDeptID AND t.DeptID = e.ParentDeptID)
)
)

You can give this a try:
SELECT e.*
FROM #Employees AS e
INNER JOIN #DepartmentBusinesses AS d
ON (d.DeptID = e.VisitingDeptID AND e.HomeDeptID = e.ParentDeptID) OR
(d.DeptID = e.ParentDeptID AND e.HomeDeptID <> e.ParentDeptID)
WHERE d.BusinessUnitCD = #SearchBusinessUnitCD

Related

Update query with Join SQL Server

I have these two tables
The first image represents ObjectData Table, and the second ColumnsSet Table.
In ObjectData the ColID attribute represents the foreign key of ColumnID in ColumnsSet Table.
I want to update a cell in Data attribute of ObjectData for example number 10 to be 20.
query :
UPDATE ObjectData SET ObjectData.Data = 'Ahmed'
FROM ColumnsSet
INNER JOIN ObjectData ON ColumnsSet.ColumnID = ObjectData.ColID
WHERE ObjectData.ColID = ColumnsSet.ColumnID
What is the correct SQL statement?
UPDATE a SET a.Data = 'Ahmed'
FROM ObjectData a
INNER JOIN ColumnsSet ON ColumnsSet.ColumnID = a.ColID
when you are using inner join it selects data which returns join condition true and not null datas
there is no need to this 'where ....'
but you have to be sure which data will update
at first use this code and select all data
SELECT *
FROM ObjectData a
INNER JOIN ColumnsSet ON ColumnsSet.ColumnID = a.ColID
and then write where clause
then change it to update
Let me show on example how to update.
At first, we should create tables:
CREATE TABLE ObjectData
(
ID INT,
CollID INT,
Data VARCHAR(50)
)
CREATE TABLE ColumnSet
(
ColumnID INT,
ColumnName VARCHAR(50)
)
Then insert data:
INSERT INTO ObjectData
(
ID,
CollID,
Data
)
VALUES
( 113, -- ID - int
1, -- CollID - int
'1' -- Data - varchar(50)
)
, (114, 5, '')
, (115, 10015, 'Mohamed')
, (116, 20026, 'Abdulghani')
INSERT INTO ColumnSet
(
ColumnID,
ColumnName
)
VALUES
( 1, -- ColumnID - int
'ID' -- ColumnName - varchar(50)
)
, (5, 'EmployeeID')
, (10015, 'FirsName')
And the final step is updating where you should write what rows need to be updated by WHERE operator:
UPDATE od
SET od.Data = 'Hey!'
FROM ObjectData od
INNER JOIN ColumnSet cs ON cs.ColumnID =od.CollID
--WHERE OD.DATA IN ('1', '10')
WHERE OD.CollID IN (1, 5)
You can write in WHERE statement any condition to choose your row which will be updated.
--Check our update statements:
SELECT
*
FROM ObjectData od
INNER JOIN #ColumnSet cs ON cs.ColumnID =od.CollID
WHERE OD.DATA IN ('1', '10')

SQL: If exists, limit user. If not exists show everything

I'm trying to found the best way to this requirements:
#fkStaffID INT = Current user.
If #fkStaffID got resource BLABLA only show rows of table X where is StaffID is here. If he DON'T have resource BLABLA, show everything.
SORRY I cannot paste full SQL, for employer's security policy. (I wish I show enough for help, not too much for security...)
What I do:
SELECT * FROM X
WHERE ((EXISTS
(SELECT 1 FROM STAFF WHERE pkStaff=#fkStaffID
AND STAFF.PkStaff IN (SELECT fkStaff FROM SECURITYSUBQUERY WHERE ResourceName='BLABLA')) AND X.fkStaff=#fkStaffID)
OR ((NOT EXISTS (SELECT 1 FROM STAFF WHERE pkStaff=#fkStaffID
AND STAFF.PkStaff IN (SELECT fkStaff FROM SECURITYSUBQUERY WHERE ResourceName='BLABLA')) )
PROBLEM: It's really slow. Can I do a more efficient way? Can I do another way? Thank you for your help!
I think you should be able to qrite the query thus:
SELECT * FROM x
WHERE #fkStaffID NOT IN (SELECT fkStaff FROM SecuritySubquery WHERE ResourceName= 'BLABLA')
OR #fkStaffID = fkStaff;
So either the #fkStaffID isn't 'BLABLA' or it matches the record's staff ID.
This NOT IN / OR still won't be very fast. Anyway, you should have the following indexes:
CREATE INDEX idx1 ON SecuritySubquery (ResourceName, fkStaff);
CREATE INDEX idx2 ON x (fkStaff);
I would try this:
if exists(select 1 from staff where pkstaff=#fkstaffid)
begin
select * from X where ResourceName = 'Blabla' and fkStaff = #fkStaffId
end
else
begin
select * from X where ResourceName = 'Blabla'
end
If we have a matching record, then we filter by that #fkStaffId, otherwise we select everything.
The below query will give you only the data for people in X who are in the STAFF table with a corresponding record in SECURITYSUBQUERY table ('BlaBla' records).
First, build test data.
IF OBJECT_ID(N'tempdb..#x') IS NOT NULL
DROP TABLE #x
CREATE TABLE #X ( fkStaff int, myStuff varchar(20) )
INSERT INTO #X ( fkStaff, myStuff )
VALUES
(1,'not me')
, (2,'not me')
, (3,'show me')
, (4,'not me')
, (5,'show me too')
IF OBJECT_ID(N'tempdb..#STAFF') IS NOT NULL
DROP TABLE #STAFF
CREATE TABLE #STAFF ( pkStaff int, name varchar(20) )
INSERT INTO #STAFF ( pkStaff, name )
VALUES
(1, 'Joe')
, (2, 'Jim')
, (3, 'Bill')
, (4, 'Ted')
, (5, 'Rufus')
IF OBJECT_ID(N'tempdb..#SECURITYSUBQUERY') IS NOT NULL
DROP TABLE #SECURITYSUBQUERY
CREATE TABLE #SECURITYSUBQUERY ( fkStaff int, ResourceName varchar(20) )
INSERT INTO #SECURITYSUBQUERY ( fkStaff, ResourceName )
VALUES
( 1, 'NotAuth' )
, ( 2, 'NotAuth' )
, ( 3, 'BlaBla' )
, ( 3, 'Extra Perm' )
, ( 4, 'NotAuth' )
, ( 5, 'BlaBla' )
Now for the query.
DECLARE #fkStaffID int ; /* Only 3 or 5 will return records. */
SELECT x.*
FROM #x x
LEFT OUTER JOIN (
SELECT s.pkStaff
FROM #STAFF s
INNER JOIN #SECURITYSUBQUERY ss ON s.pkStaff = ss.fkStaff
AND ss.ResourceName = 'BlaBla'
WHERE s.pkStaff = #fkStaffID
) t ON t.pkStaff = x.fkStaff
WHERE t.pkStaff IS NOT NULL
AND x.fkStaff = #fkStaffID
This will only give results if users Bill or Rufus are logged in (and passed as #fkStaffID).
I don't know how well this will scale, but the optimizer should work faster than EXISTS or NOT IN subqueries. Try it with your data.

How to get id's of parent ids for inserting children

I have Parent and Child table.
The goal is to duplicate the records, except with new primary keys.
Original Tables
Parent(id)
1
Child(id,parentId, data)
1,1
2,1
After insert:
Parent
1
2
Child
1,1
2,1
3,2
4,2
How do I do that? The part I am having trouble with is getting the new parent key for use with the child records.
This is what I have come up with so far.
--DECLARE VARS
declare #currentMetadataDocumentSetId int = 1, --Ohio
#newMetadataDocumentSetid int = 3; --PA
--CLEANUP
IF OBJECT_ID('tempdb..#tempFileRowMap') IS NOT NULL
/*Then it exists*/
DROP TABLE #tempFileRowMap
--Remove existing file row maps.
delete from file_row_map where metadata_document_set_id = #newMetadataDocumentSetid;
--Create a temptable to hold data to be copied.
Select [edi_document_code],
[functional_group],
[description],
3 as [metadata_document_set_id],
[document_name],
[incoming_file_row_subtype],
[metadata_document_id],
[document_subcode],
[outgoing_file_row_subtype],
[asi_type_code],
[asi_action_code],
[metadata_document_set],
file_row_map_id as orig_file_row_map_id
into #tempFileRowMap
from file_row_map fileRowMap
where metadata_document_set_id = #currentMetadataDocumentSetId;
--Select * from #tempFileRowMap;
Insert into file_row_map select
[edi_document_code],
[functional_group],
[description],
[metadata_document_set_id],
[document_name],
[incoming_file_row_subtype],
[metadata_document_id],
[document_subcode],
[outgoing_file_row_subtype],
[asi_type_code],
[asi_action_code],
[metadata_document_set]
from #tempFileRowMap
--Show Results
Select * from file_row_map fileRowMap where fileRowMap.metadata_document_set_id = #newMetadataDocumentSetid
--Update Detail
Select
[file_row_map_id],
[file_row_column],
[element_code],
[element_metadata_id],
[col_description],
[example],
[translate],
[is_used],
[is_mapped],
[page_num],
[subcode],
[qualifier],
[loop_code],
[loop_subcode],
[default_value],
[delete_flag]
into #tempFileRowMapDetail
from [dbo].[file_row_map_detail] d
left join #tempFileRowMap m
on m.orig_file_row_map_id = d.file_row_map_id
select * from #tempFileRowMapDetail
Simply use OUTPUT clause for getting exact Parent Table Primary Key values.
Lets build Example Schema for your case
--For Capturing inserted ID
CREATE TABLE #ID_CAPTURE (PARENT_ID INT,ORDER_NME VARCHAR(20));
--Your Intermidiate Data To insert into Actual Tables
CREATE TABLE #DUMMY_TABLE (ORDER_NME VARCHAR(20), ITEM_NME VARCHAR(20));
--Actual Tables
CREATE TABLE #ORDER_PARENT (ORDER_ID INT IDENTITY,ORDER_NME VARCHAR(20))
CREATE TABLE #ORDER_CHILD (CHILD_ID INT IDENTITY ,ORDER_ID INT, ORDER_NME VARCHAR(20))
INSERT INTO #DUMMY_TABLE
SELECT 'BILL1','Oil'
UNION ALL
SELECT 'BILL1', 'Gas'
UNION ALL
SELECT 'BILL2', 'Diesel'
Now do Inserts in Parent & Child Tables
INSERT INTO #ORDER_PARENT
OUTPUT inserted.ORDER_ID, inserted.ORDER_NME into #ID_CAPTURE
SELECT DISTINCT ORDER_NME FROM #DUMMY_TABLE
INSERT INTO #ORDER_CHILD
SELECT C.PARENT_ID, ITEM_NME FROM #DUMMY_TABLE D
INNER JOIN #ID_CAPTURE C ON D.ORDER_NME = C.ORDER_NME
SELECT * FROM #ID_CAPTURE
SELECT * FROM #ORDER_CHILD
There are other ways to get Inserted Identity values.
See documentation ##IDENTITY (Transact-SQL) , SCOPE_IDENTITY
Try following approach:
DECLARE #Table1 TABLE (
ID INT NOT NULL PRIMARY KEY,
ParentID INT NULL, -- FK
[Desc] VARCHAR(50) NOT NULL
);
INSERT #Table1 (ID, ParentID, [Desc])
VALUES
(1, NULL, 'A'),
(2, 1, 'AA.1'),
(3, 1, 'AA.2'),
(4, NULL, 'B'),
(5, 4, 'BB.1'),
(6, 4, 'BB.2'),
(7, 4, 'BB.3'),
(8, 7, 'BBB.1');
DECLARE #ParentID INT = 4;
DECLARE #LastID INT = (SELECT TOP(1) ID FROM #Table1 x ORDER BY x.ID DESC)
IF #LastID IS NULL
BEGIN
RAISERROR('Invalid call', 16, 1)
--RETURN ?
END
SELECT #LastID AS LastID;
/*
LastID
-----------
8
*/
DECLARE #RemapIDs TABLE (
OldID INT NOT NULL PRIMARY KEY,
[NewID] INT NOT NULL UNIQUE
);
WITH CteRecursion
AS (
SELECT 1 AS Lvl, crt.ID, crt.ParentID --, crt.[Desc]
FROM #Table1 crt
WHERE crt.ID = #ParentID
UNION ALL
SELECT cld.Lvl + 1 AS Lvl, crt.ID, crt.ParentID --, crt.[Desc]
FROM #Table1 crt
JOIN CteRecursion cld ON crt.ParentID = cld.ID
)
INSERT #RemapIDs (OldID, [NewID])
SELECT r.ID, #LastID + ROW_NUMBER() OVER(ORDER BY r.Lvl) AS [NewID]
FROM CteRecursion r;
--INSERT #Table1 (ID, ParentID, [Desc])
SELECT nc.[NewID] AS ID, np.[NewID] AS ParentID, o.[Desc]
FROM #Table1 o -- old
JOIN #RemapIDs nc /*new child ID*/ ON o.ID = nc.OldID
LEFT JOIN #RemapIDs np /*new parent ID*/ ON o.ParentID = np.OldID
/*
ID ParentID Desc
----------- ----------- --------------------------------------------------
9 NULL B
10 9 BB.1
11 9 BB.2
12 9 BB.3
13 12 BBB.1
*/
Note: with some minor changes should work w. many ParentIDs values.

Insert into table if record does not already exist

For some reason, this is giving me the "cannot insert duplicate record into table" error.
INSERT INTO [DMS].[dbo].[Deductions]
(
CustomerID,
DeductionCode,
DeductionDescription
)
SELECT b.CustomerID,
b.AdjustmentReason,
b.AdjustmentReason
FROM #CreditDebitAdjustmentDetail b
WHERE NOT EXISTS ( SELECT 1
FROM [DMS].[dbo].[Deductions]
WHERE CustomerID = b.CustomerID
AND DeductionCode = b.AdjustmentReason )
The weird thing is, I tested it as such:
DECLARE #CreditDebitAdjustmentDetail TABLE
(
CustomerID INT,
AdjustmentReason VARCHAR(50)
)
INSERT INTO #CreditDebitAdjustmentDetail
(CustomerID, AdjustmentReason)
VALUES (143, -- CustomerID - int
'024' -- AdjustmentReason - varchar(50)
)
INSERT INTO [DMS].[dbo].[Deductions]
(
CustomerID,
DeductionCode,
DeductionDescription
)
SELECT b.CustomerID,
b.AdjustmentReason,
b.AdjustmentReason
FROM #CreditDebitAdjustmentDetail b
WHERE NOT EXISTS ( SELECT 1
FROM [DMS].[dbo].[Deductions]
WHERE CustomerID = b.CustomerID
AND DeductionCode = b.AdjustmentReason )
And it DOES NOT insert into the table because the record already exists.
Am I missing something here?
EDIT - I thought I had fixed it by doing this but I'm still getting the same error:
INSERT INTO [DMS].[dbo].[Deductions]
(
CustomerID,
DeductionCode,
DeductionDescription
)
SELECT a.CustomerID,
a.AdjustmentReason,
a.AdjustmentReason
FROM #CreditDebitAdjustmentDetail a
WHERE NOT EXISTS ( SELECT 1
FROM [DMS].[dbo].[Deductions] b
WHERE a.CustomerID = b.CustomerID
AND a.AdjustmentReason = b.DeductionCode )
I figured it out, DOH!
KEYWORD ... DISTINCT -_-
INSERT INTO [DMS].[dbo].[Deductions]
(
CustomerID,
DeductionCode,
DeductionDescription
)
SELECT DISTINCT
a.CustomerID,
ISNULL(a.AdjustmentReason, 'UNKNOWN'),
ISNULL(a.AdjustmentReason, 'UNKNOWN')
FROM #CreditDebitAdjustmentDetail a
WHERE NOT EXISTS ( SELECT 1
FROM [DMS].[dbo].[Deductions] b
WHERE a.CustomerID = b.CustomerID
AND CASE a.AdjustmentReason
WHEN NULL THEN 'UNKNOWN'
WHEN '' THEN 'UNKNOWN'
END = b.DeductionCode )

SQL Match multiple rows

relationID sessionID_Ref userID_Ref
1 1 1
2 1 2
3 2 1
4 2 3
5 3 1
6 3 2
7 3 3
Okey! I'm building a messaging system with the possibility to send messages to a group of people. But I'm stuck with this SQL query where to find the sessionID depending on what users I send the message to.
For example: If I (userID: 1) send a message to userID 2, the SQL Query should return sessionID: 1
If I send a message to userID 2 and 3, it sould return: sessionID: 3
Can I do this with a single sql query, using MSSQL?
Possibly something like this:
select sessionID_Ref
from tablename
group by sessionID_Ref
having count(distinct userID_Ref) = 2
and min(userID_Ref) = 1
and max(userID_Ref) = 2
Here's a full example:
create table #tablename (
relationID int,
sessionID_Ref int,
userID_Ref int
)
insert into #tablename values(1,1,1)
insert into #tablename values(2,1,2)
insert into #tablename values(3,2,1)
insert into #tablename values(4,2,3)
insert into #tablename values(5,3,1)
insert into #tablename values(6,3,2)
insert into #tablename values(7,3,3)
create table #users (
users int
)
insert into #users values(1)
insert into #users values(3)
select t.sessionID_Ref from #tablename t
inner join #users u on t.userID_Ref = u.users
inner join (
select t.sessionID_Ref
from #tablename t
group by t.sessionID_Ref
having COUNT(t.userID_Ref) = (select COUNT(*) from #users)
) aux on aux.sessionID_Ref = t.sessionID_Ref
group by t.sessionID_Ref
having COUNT(t.userID_Ref) = (select COUNT(*) from #users)
drop table #tablename
drop table #users
SELECT TOP 1 sessionID_Ref
FROM table AS table_2
WHERE (userID_Ref = 28 OR
userID_Ref = 11) AND
((SELECT COUNT(*) AS Expr1
FROM table AS table_1
WHERE (sessionID_Ref = table_2.sessionID_Ref) AND (userID_Ref = 28) OR
(sessionID_Ref = table_2.sessionID_Ref) AND (userID_Ref = 11)) = 2) AND
((SELECT COUNT(*) AS Expr1
FROM table AS table_1
WHERE (sessionID_Ref = table_2.sessionID_Ref)) = 2)
This works, but there must be a faster way....

Resources