TSQL Selection Criteria For Join - Left Join - sql-server

Select *
From cacheAttendanceMeasures cam
Left Join dmcUserSelectedAttendanceMeasures usam on usam.attMeasureID = cam.attMeasureID
And usam.personID = #personID
And usam.pageID = #pageID
I understand that the above query will return all rows from cacheAttendanceMeasures as well as any rows from dmcUserSelectedAttendanceMeasures and dmcUserSelectedStudentMonths where the conditions match. What I would really like it to do is this:
If the left join(s) don't match, then return all rows from cacheAttendanceMeasures -- so what it is currently doing
However, if the left join(s) DO match, then return ONLY the rows that match from cacheAttendanceMeasures
Is this possible?
EDIT:
I simplified the query above to only include one Left Join table, I don't want to over-complicate the issue.
Below are dataset examples I'd like to see returned based on if there are no matches between the tables versus there is a match:
cam Table
cID attMeasureID Value
1 1 530
2 2 95.7
3 3 380
4 4 742.57
5 5 200
usam Table
uID personID pageID attMeasureID
1 877450 31 1
2 923450 28 2
3 877450 31 3
4 369842 28 4
5 212193 25 1
Dataset to Return if #personID = 577597 & #pageID = 20:
CID attMeasureID Value uID pageID attMeasureID
1 1 530 null nul null
2 2 95.7 null null null
3 3 380 null null null
4 4 742.57 null null null
5 5 200 null null null
Dataset to Return if #personID = 877450 & #pageID = 31:
CID attMeasureID Value uID personID pageID attMeasureID
1 1 530 1 877450 31 1
3 3 380 3 877450 31 3

I'm not really sure what results you want...
At first I thought maybe something like this..
SELECT *
FROM cacheAttendanceMeasures cam
LEFT JOIN dmcUserSelectedAttendanceMeasures usam
on usam.attMeasureID = cam.attMeasureID
And usam.personID = #personID
And usam.pageID = #pageID
LEFT JOIN dmcUserSelectedStudentMonths ussm
on ussm.monthSeq = cam.pupilMonth
And ussm.personID = USAM.PersonID
And ussm.pageID = USAM.PageId
--This where clause would seem silly as it negates the left joins; making them inner joins and violates #1 as to what you're after. so I don't think that's what you're after...
WHERE ussm.personID is not null
and usam.personID is not null
So with rule 2 are you saying... if a single record isn't null, then only return records which exist in both Left joined tables and the 1st table?
So given:
T1 T2 T3
X X X
Y NULL Y
Z Y NULL
T NULL NULL
I think you would want just record X
But Given
So given:
T1 T2 T3
X X NULL
Y NULL Y
Z Y NULL
T NULL NULL
I think you would want X, Y Z, and T... need better clairification.

Related

SQL Check to see if any value in a list exists in another table

I have a temp table that looks something like this:
Record DepartmentId PositionId EmployeeId StatusId CustomerId
1 Null Null Null 4
2 7 454 Null Null
3 Null 454 Null 3
3 Null Null Null Null 214
3 Null Null Null Null 100
3 Null Null Null Null 312
4 Null Null Null Null 357
I inserted the above into the temp table from tables that looked like this:
Record Table Record-to-Department Record-to-Position
Record Name Record DepartmentId Record PositionId
1 Red 2 7 2 454
2 blue 3 454
3 Green
4 Purple
Record-To-Status Record-To-Customer
Record StatusId Record CustomerId
1 4 3 214
3 3 3 100
3 312
4 357
I have an employee whose record looks something like this:
EmployeeId DepartmentId PositionId StatusId
342 7 454 4
Employee Customers:
EmployeeId CustomerId
342 357
342 95
342 720
In this scenario, it would return Record 1 (because it matches the StatusId), Record 2 (because it matches both the DepartmentId and the PositionId), but it would not return Record 3 because it only matches the PositionId and not the StatusId, and it would return RecordId 4 because one of the Employee CustomerIds matches the CustomerId on Record 4.
I got part of this answer on another question enter link description here (please forgive me I am new and trying to figure out how to ask everything I need to know), but I can't figure out how to handle the multi-records.
I tried selecting the Employees customer Id's into a table variable and then attempted to use the Coalesce like this:
Declare #Customers table(CustomerId int)
INSERT INTO #Customers(CustomerId)
SELECT DISTINCT S.CustomerId
FROM employee_Customers
Select * from tbl
WHERE
COAlesce(StatusId,#StatusId)=#StatusId AND
COALESCE(DepartmentId,#DepartmentId)=#DepartmentId AND
Coalesce(PositionId,#PositionId)=#PositionId AND
Coalesce(EmployeeCompanyId,#EmployeeCompanyId) = #EmployeeCompanyId AND
COALESCE((Select CustomerId from tbl_Requirement_to_Customer),(Select CustomerId from #Customers)) = (Select CustomerId from #Customers)
But I receive the error "Subquery Returned more than 1 value".
I have a possible solution you can try. I don't think it will be plug-and-play but hopefully you can adapt it to your situation. I am using just the data as presented in your temp table, your employee record and your Employee-customers correlation.
The basic logic is to join your temp table to the employee(s) using or condition, but then to get a count of populated values, and compare this count to a count of the number of matching values, which must be at least the first count and greater than zero.
This returns your desired output:
select t.*
from Temp t
left join emp e on e.DepartmentId=t.DepartmentId or e.PositionId=t.PositionId or e.EmployeeId=t.EmployeeId or e.StatusId=t.StatusId
outer apply (
select case when exists (
select * from EmployeeCustomers ec join emp e on e.EmployeeId=ec.EmployeeId where ec.CustomerID=t.CustomerId
) then 1 else 0 end CustomerIdMatch
)c
outer apply (
values (
Iif(t.departmentId is null,0,1) +
Iif(t.PositionId is null,0,1) +
Iif(t.EmployeeId is null,0,1) +
Iif(t.StatusId is null,0,1) +
c.CustomerIdMatch
))x(Cnt)
outer apply (
values (
Iif(t.departmentId=e.DepartmentId,1,0) +
Iif(t.PositionId=e.PositionId,1,0) +
Iif(t.EmployeeId=e.EmployeeId,1,0) +
Iif(t.StatusId=e.StatusId,1,0) +
c.CustomerIdMatch
))y(Cnt2)
where cnt2>=cnt and cnt2>0
See working DB<>Fiddle

T-SQL Get Values from Lookup table and use in view / stored procedure

I couldn't find that via search, so I guess I am not asking it the right way, so help is welcome.
We have a lookup table:
Id Name
------------------
1 "Test"
2 "New"
3 "InProgress"
Table2:
StatusId SomethingElse
1
2
Table 1
ID Other Other StatusId (Fkey to Table2) ...
Then we have a view that selects from several tables and one of the columns is a CASE Statement:
SELECT * FROM Table1 t1 -- has million records
CASE When t1.StatusId = 1 THEN (SELECT Name from LOOKUP table where ID = 1) END --'Test'
CASE When t1.StatusId = 2 THEN (SELECT Name from LOOKUP table where ID = 2) END --'New'
CASE When t3.Date is not null THEN (SELECT Name from LOOKUP table where ID = 3) END --'In Progress'
-- AND ALSO the case look at other tables another 5-6 tables and there are conditions from there
INNER JOIN Table2 t2 on ...
INNER JOIN Table3 t3 on ...
As you see these are really static values.
I want to load them once into variables, e.g.
#LookUp1 = SELECT [NAME] FROM LookUP WHERE Id = 1,
#LookUp2 = SELECT [NAME] FROM LookUP WHERE Id = 2
and replace the select in the CASE statement to this:
When StatusId = 1 THEN #LookUp
When StatusId = 2 THEN #LookUp2
The view loops through millions of records and it gets really slow to do the select from Lookup table for every row.
Why not simply use a join?
SELECT <columns list from main table>, Lt.Name
FROM <main table> As Mt -- Do not use such aliases in real code!
JOIN <SecondaryTable> As St -- this represents your Table3
ON <condition>
[LEFT] JOIN <Lookup table> As Lt
ON Mt.StatusId = Lt.Id
OR (Lt.Id = 3 AND St.Date is not null)
Of course, replace <columns list from main table> with the actual columns list, <main table> with the name of the main table and so on.
The join might be an inner or left join, depending on the nullability of the StatusId column in the main table and if it's nullable, on what you want to get in such cases (either a row with null name or no row at all).
I've put together a little demonstration to show you exactly what I mean.
Create and populate sample tables (Please save us this step in your future questions):
CREATE TABLE LookUp (Id int, Name varchar(10));
INSERT INTO LookUp (Id, Name) VALUES
(1, 'Test'), (2, 'New'), (3, 'InProgress');
CREATE TABLE Table1 (Id int not null, StatusId int null);
INSERT INTO Table1(Id, StatusId)
SELECT n, CASE WHEN n % 3 = 0 THEN NULL ELSE (n % 3) END
FROM
(
SELECT TOP 30 ROW_NUMBER() OVER(ORDER BY ##SPID) As n
FROM sys.objects
) tally
CREATE TABLE Table3
(
Id int not null,
Date date null
)
INSERT INTO Table3 (Id, Date)
SELECT Id, CASE WHEN StatusId IS NULL AND Id % 4 = 0 THEN GetDate() END
FROM Table1
The query:
SELECT Table1.Id,
Table1.StatusId,
Table3.Date,
LookUp.Name
FROM Table1
JOIN Table3
ON Table1.Id = Table3.Id
LEFT JOIN LookUp
ON Table1.StatusId = LookUp.Id
OR (LookUp.Id = 3 AND Table3.Date IS NOT NULL)
Results:
Id StatusId Date Name
1 1 NULL Test
2 2 NULL New
3 NULL NULL NULL
4 1 NULL Test
5 2 NULL New
6 NULL NULL NULL
7 1 NULL Test
8 2 NULL New
9 NULL NULL NULL
10 1 NULL Test
11 2 NULL New
12 NULL 27.06.2019 InProgress
13 1 NULL Test
14 2 NULL New
15 NULL NULL NULL
16 1 NULL Test
17 2 NULL New
18 NULL NULL NULL
19 1 NULL Test
20 2 NULL New
21 NULL NULL NULL
22 1 NULL Test
23 2 NULL New
24 NULL 27.06.2019 InProgress
25 1 NULL Test
26 2 NULL New
27 NULL NULL NULL
28 1 NULL Test
29 2 NULL New
30 NULL NULL NULL
You can also see a live demo on rextester.
Create a SQL function which return Name according to Id.
Create FUNCTION [dbo].[GetLookUpValue]
(
#Id int
)
RETURNS varchar(500)
AS BEGIN
return(Select Name from LOOKUP_table with(nolock) where Id=#Id)
END

SQL Server : compare all values in two tables

How to get the following result using this two tables
TipoComponenteID1 TipoComponenteID2
-------------------------------------
1 1
NULL 3
6 NULL
8 NULL
9 NULL
10 NULL
TipoComponenteID1
TipoComponenteID1
-----------------
1
6
8
9
10
TipoComponenteID2
TipoComponenteID2
-----------------
1
3
This can be solved with a simple FULL OUTER JOIN.
SELECT
TCID1 = t1.[your id column here]
,TCID2 = t2.[your id column here]
FROM TipoComponenteID1 t1
FULL OUTER JOIN TipoComponenteID2 t2 ON t1.[your id column here] = t2.[your id column here]

SQL Update row column with random lookup value

I am trying to update a lead table to assign a random person from a lookup table. Here is the generic schema:
TableA (header),
ID int,
name varchar (30)
TableB (detail),
ID int,
fkTableA int, (foreign key to TableA.ID)
recordOwner varchar(30) null
other detail colums..
TableC (owners),
ID int,
fkTableA int (foreign key to TableA.ID)
name varchar(30)
TableA has 10 entries, one for each type of sales lead pool. TableB has thousands of entries for each row in TableA. I want to assign the correct recordOwners from TableC to and even number of rows each (or as close as I can). TableC will have anywhere from one entry for each row in tableA or up to 10.
Can this be done in one statement? It doesn't have to be. I can't seem to figure out the best approach for speed. Any thoughts or samples are appreciated.
Updated:
TableA has a 1 to many relation ship with TableC. For every record of TableA, TableC will have at least one row, which represents an owner that will need to be assigned to a row in TableB.
TableA
int name
1 LeadSourceOne
2 LeadSourceTwo
TableC
int(id) int(fkTableA) varchar(name)
1 1 Tom
2 1 Bob
3 2 Timmy
4 2 John
5 2 Steve
6 2 Bill
TableB initial data
int(id) int(fkTableA) varchar(recordOwner) (other detail columns)
1 1 NULL ....
2 1 NULL ....
3 1 NULL ....
4 2 NULL ....
5 2 NULL ....
6 2 NULL ....
7 2 NULL ....
8 2 NULL ....
9 2 NULL ....
TableB end result
int(id) int(fkTableA) varchar(recordOwner) (other detail columns)
1 1 TOM ....
2 1 BOB ....
3 1 TOM ....
4 2 TIMMY ....
5 2 JOHN ....
6 2 STEVE ....
7 2 BILL ....
8 2 TIMMY ....
9 2 BILL ....
Basically I need to randomly assign a record from tableC to tableB based on the relationship to tableA.
UPDATE TabB SET name = (SELECT TOP 1 coalesce(tabC.name,'') FROM TabC INNER JOIN TabA ON TabC.idA = TabA.id WHERE tabA.Id = TabB.idA )
Should work but its not tested.
Try this:
UPDATE TableB
SET recordOwner = (SELECT TOP(1) [name]
FROM TableC
ORDER BY NEWID())
WHERE recordOwner IS NULL
I ended up looping thru and updating x percent of the detail records based on how many owners I had. The end result is something like this:
create table #tb_owners(userId varchar(30), processed bit)
insert into #tb_owners(
userId,
processed)
select userId = name,
processed = 0
from tableC
where fkTableA = 1
select #percentUpdate = cast(100 / count(*) as numeric(8,2))
from #tb_owners
while exists(select 1 from #tb_owners o where o.processed = 0)
begin
select top 1
#userFullName = o.name
from #tb_owners o
where o.processed = 0
order by newId()
update tableB
set recordOwner = #userFullName
from tableB ptbpd
inner join (
select top (#percentUpdate) percent
id
from tableB
where recordOwner is null
order by newId()
) nd on (ptbpd.id = nd.id)
update #tb_owners
set processed = 1
where userId = #oUserId
end
--there may be some left over, set to last person used
update tableB
set recordOwner = #userFullName
from tableB
where ptbpd.recordOwner is null

Update rows content adding values from rows in another table

I've already tried all things from here: Update rows from another table
with no success.
I'm using SQLite, and I'm executing the query with SQLite Database Browser.
I have 3 tables Off, Vio and Loc with data like this
Off(Aid,ox,oy):
0 0 0
1 100 100
2 200 200
Vio(Vid,Aid):
0 0
1 1
2 2
3 1
Loc(vid,x,y):
0 1 2
1 5 6
2 9 1
2 2 3
3 4 4
Aid is primary key in Off and foreign key in Vio.
Vid is primary key in Vio and foreign key in Loc.
I want to update the Loc rows adding the correct ox and oy to the x and y value to get something like this as a result:
"updated" Loc(vid,x,y)
0 1 2
1 105 106
2 209 201
2 202 203
3 104 104
I've tried this:
WITH CTE AS (
SELECT Loc.x, Loc.y, Off.ox AS offx, Off.oy AS offy
FROM Loc, Vio, Off
WHERE Vio.Aid=Off.Aid AND Vio.vid=Loc.vid
)
UPDATE CTE
SET x= x + offx, y= y + offy
And this:
UPDATE Loc, Off
SET x=x+Off.ox, y=y+Off.oy
FROM (
SELECT Off.ox, Off.oy
FROM Loc, Vio, Off
WHERE Vio.Aid = Off.Aid AND Vio.vid=Loc.vid
)
Within SQLite Database Browser with no success even when the SELECT query gives me all the data I need for the update.
As mentioned in the duplicate question, you have to use a correlated subquery. Since you're updating two columns, you need to use a subquery for each of them:
UPDATE Loc
SET x = (
SELECT L1.x + Off.ox
FROM Loc l1
JOIN Vio ON L1.vid = Vio.vid
JOIN Off ON Vio.Aid = Off.Aid
WHERE Loc.vid = l1.vid),
y = (
SELECT L1.y + Off.oy
FROM Loc l1
JOIN Vio ON L1.vid = Vio.vid
JOIN Off ON Vio.Aid = Off.Aid
WHERE Loc.vid = l1.vid)
DEMO

Resources