I try to perform an update on the same table depending on conditions. I know I can do the updates one by one. I tried
IF EXISTS (SELECT *
FROM tblA a
INNER JOIN tblB b ON a.id = b.id
WHERE a.number = 1 AND a.code BETWEEN 1 AND 20)
BEGIN
UPDATE tblA
SET name = b.name, zipCode = 'J2J'
END
ELSE
BEGIN
UPDATE tblA
SET city = b.city
END
I get the error:
multi-part identifier b.name, b.city could not be bound.
I would like to make the code more readable. So, is there a way to do it? Need help please.
If you want to access another table in your update you have to join that table on, and alias your tables and then UPDATE the primary table alias.
Also you can carry out your entire update in one go using a CASE expression to conditionally update the required columns. Its always best to use set based operations rather than procedural within SQL Server.
UPDATE A SET
[Name] = CASE WHEN A.number = 1 AND A.code BETWEEN 1 AND 20 THEN B.[Name] ELSE A.[NAME] END
, ZipCode = CASE WHEN A.number = 1 AND A.code BETWEEN 1 AND 20 THEN 'J2J' ELSE A.ZipCode END
, City = CASE WHEN A.number = 1 AND A.code BETWEEN 1 AND 20 THEN A.City ELSE B.City END
FROM tblA AS A
INNER JOIN tblB AS B ON A.id = B.id;
Although as this repeats the same CASE logic 3 times I would be inclined to calculate it once in a CROSS APPLY e.g.
UPDATE A SET
[Name] = CASE WHEN X.ConditionMet = 1 THEN B.[Name] ELSE A.[NAME] END
, ZipCode = CASE WHEN X.ConditionMet = 1 THEN 'J2J' ELSE A.ZipCode END
, City = CASE WHEN X.ConditionMet = 1 THEN A.City ELSE B.City END
FROM tblA AS A
INNER JOIN tblB AS B ON A.id = B.id
CROSS APPLY (VALUES (CASE WHEN A.number = 1 AND A.code BETWEEN 1 AND 20 THEN 1 ELSE 0 END)) AS X (ConditionMet);
I highly recommend reading the official docs as there are many clear examples there.
This is based off another one of my questions.. see Update table set column equal to select where id matches outside of query in oracle
Is it possible to use a LOOP or cursor to update my table SNP_FACULTY_CENSUS for which the PERSON_SKEY VALUES match IN SNP_FACULTY_CENSUS AND VPAA_PPAGGENL_FIS ?
I ask this because I have an update statement that works perfectly fine when I explicitly define the ID column (Person_Skey)
For example:
WHERE V.PERSON_SKEY = 12345
However, if I use a where clause which gets the value from another table
WHERE V.PERSON_SKEY = SNP_FACULTY_CENSUS.PERSON_SKEY
The update statement does not update the columns correctly (I think it uses the wrong value for MAX(OIR_DEGREE_HIERARCHY))
Here is my code where I wish to loop with the different person_skey values in SNP_FACULTY_CENSUS:
WITH HIGHES_DEG_VALUE AS (
SELECT max(D.OIR_DEGREE_HIERARCHY) AS MAX_DEG
FROM VPAA_PPAGGENL_FIS V
JOIN DIM_DEGREE_CROSSWALK D
ON V.VPAA_FACULTY_DEGREE_CODE =
D.VPAA_DEGREE_CODE
WHERE V.PERSON_SKEY = 189444) --CHANGE TO = SRC.PERSON_SKEY
SELECT
CASE
WHEN MAX_DEG = 30
THEN --CHECK IF EXIST ON CROSSWALKS AND USE THAT VALUE IF IT DOES
(CASE
WHEN EXISTS (SELECT V.VPAA_FACULTY_DEGREE_CODE, --IF ON SUBJ CODE CROSSWALK
V.VPAA_FACULTY_SUBJECT_CODE
FROM VPAA_PPAGGENL_FIS V
JOIN DIM_DEGREE_CROSSWALK D
ON V.VPAA_FACULTY_DEGREE_CODE = D.VPAA_DEGREE_CODE
INNER JOIN SUBJ_CODE_CROSSWALK S
ON V.VPAA_FACULTY_DEGREE_CODE = S.VPAA_FACULTY_DEGREE_CODE
and S.VPAA_FACULTY_SUBJECT_CODE = V.VPAA_FACULTY_SUBJECT_CODE
WHERE V.PERSON_SKEY =189444 ----CHANGE TO = SRC.PERSON_SKEY
)
THEN (SELECT DISTINCT -- USE TERMINAL VALUE FROM SUBJ CODE
CASE
WHEN s.OIR_TERMINAL_DEGREE_STATUS =
'Terminal'
THEN
'Yes'
WHEN s.OIR_TERMINAL_DEGREE_STATUS =
'Not terminal'
THEN
'No'
ELSE
'Unknown'
END
FROM SUBJ_CODE_CROSSWALK S
INNER JOIN VPAA_PPAGGENL_FIS V
ON V.VPAA_FACULTY_DEGREE_CODE =
S.VPAA_FACULTY_DEGREE_CODE
AND S.VPAA_FACULTY_SUBJECT_CODE =
V.VPAA_FACULTY_SUBJECT_CODE
WHERE V.PERSON_SKEY = 189444 )----CHANGE TO = SRC.PERSON_SKEY )
WHEN EXISTS (SELECT DISTINCT V.VPAA_FACULTY_DEGREE_CODE, --IF ON CLIC INSTRUCTORS CROSSWALK
V.VPAA_FACULTY_SUBJECT_CODE,
I.OIR_EMPLOYEE_DEPARTMENT_HOME_ORG
FROM VPAA_PPAGGENL_FIS V
INNER JOIN CLIC_INSTRUCTORS_CROSSWALK C
ON V.VPAA_FACULTY_DEGREE_CODE = C.VPAA_FACULTY_DEGREE_CODE
AND V.VPAA_FACULTY_SUBJECT_CODE = C.VPAA_FACULTY_SUBJECT_CODE
INNER JOIN IPEDS_REPORT_DETAILS I ON V.PIDM = I.HR_PIDM
WHERE V.PERSON_SKEY = 189444 ----CHANGE TO = SRC.PERSON_SKEY
AND (I.OIR_EMPLOYEE_DEPARTMENT_HOME_ORG = '396000'
OR I.OIR_EMPLOYEE_DEPARTMENT_HOME_ORG = '396010')
)
THEN (SELECT DISTINCT
CASE
WHEN C.OIR_TERMINAL_DEGREE_STATUS =
'Terminal'
THEN
'Yes'
WHEN C.OIR_TERMINAL_DEGREE_STATUS =
'Not terminal'
THEN
'No'
ELSE
'Unknown'
END
FROM CLIC_INSTRUCTORS_CROSSWALK C
INNER JOIN VPAA_PPAGGENL_FIS V
ON C.VPAA_FACULTY_DEGREE_CODE =
V.VPAA_FACULTY_DEGREE_CODE
AND C.VPAA_FACULTY_SUBJECT_CODE =
V.VPAA_FACULTY_SUBJECT_CODE
WHERE V.PERSON_SKEY = 189444 )----CHANGE TO = SRC.PERSON_SKEY)
ELSE --USE DIM DEGREE CROSSWALK NORMALLY
(SELECT DISTINCT
CASE
WHEN D.OIR_TERMINAL_DEGREE_STATUS =
'Terminal'
THEN
'Yes'
WHEN D.OIR_TERMINAL_DEGREE_STATUS =
'Not terminal'
THEN
'No'
ELSE
'Unknown'
END
FROM DIM_DEGREE_CROSSWALK D
JOIN VPAA_PPAGGENL_FIS V
ON V.VPAA_FACULTY_DEGREE_CODE =
D.VPAA_DEGREE_CODE
WHERE V.PERSON_SKEY = 189444 ----CHANGE TO = SRC.PERSON_SKEY)
ORDER BY OIR_DEGREE_HIERARCHY DESC
FETCH FIRST ROW ONLY )
END) --USE DIM DEGREE CROSSWALK FOR THE TERMINAL VALUE FOR THE MAX DEG THERE
WHEN MAX_DEG != 30
THEN
(SELECT DISTINCT
CASE
WHEN D.OIR_TERMINAL_DEGREE_STATUS =
'Terminal'
THEN
'Yes'
WHEN D.OIR_TERMINAL_DEGREE_STATUS =
'Not terminal'
THEN
'No'
ELSE
'Unknown'
END
FROM DIM_DEGREE_CROSSWALK D
JOIN VPAA_PPAGGENL_FIS V
ON V.VPAA_FACULTY_DEGREE_CODE =
D.VPAA_DEGREE_CODE
WHERE V.PERSON_SKEY = 189444 ----CHANGE TO = SRC.PERSON_SKEY)
ORDER BY OIR_DEGREE_HIERARCHY DESC
FETCH FIRST ROW ONLY )
ELSE NULL
END
FROM HIGHES_DEG_VALUE
My View shows Duplicate row which i don't want.
I am geting
1, YM
1, NULL
2, YM
2, NULL
With below Code
SELECT
dbo.Store.SID,
CASE WHEN dbo.Store.SID <> dbo.FileStore.SID THEN NULL
WHEN dbo.FileStore.MailSent = 'M' THEN 'YM'
WHEN dbo.FileStore.SID = dbo.Store.SID AND dbo.FileStore.FileType = 1 THEN 'Y'
ELSE NULL END AS FM
FROM
dbo.STORE
INNER JOIN dbo.FileStore ON dbo.Store.SID = dbo.FileStore.SID
I am looking for
1 YM
2 YM
You appear to want filtering. If I understand correctly:
SELECT s.SID,
(CASE WHEN fs.MailSent = 'M' THEN 'YM'
WHEN fs.FileType = 1 THEN 'Y'
END) AS FM
FROM dbo.STORE s INNER JOIN
dbo.FileStore fs
ON s.SID = fs.SID
WHERE fs.MailSent = 'M' OR fs.FileType = 1;
There is no reason to repeat the JOIN conditions in the CASE expression. You know they are true because of the JOIN.
SELECT
dbo.Store.SID
,CASE
WHEN dbo.Store.SID <> dbo.FileStore.SID THEN NULL --will never happen since it's an inner join
WHEN dbo.FileStore.MailSent = 'M' THEN 'YM'
WHEN dbo.FileStore.SID = dbo.Store.SID --will happen always since it's an inner join
AND dbo.FileStore.FileType = 1 THEN 'Y'
ELSE NULL -- this is the cause for Null, you have FileStore.MailSent <> 'M'
END AS FM
FROM dbo.STORE
INNER JOIN dbo.FileStore
ON dbo.Store.SID = dbo.FileStore.SID
How can I convert this simple Outer Join to MERGE to update FT.SS to a certain value for the selected rows:
SELECT FT.SS FROM FT_T FT LEFT OUTER JOIN DC_T DC
ON FT.ID = DC.ID AND FT.CN = DC.CN
WHERE FT.GID = 'AB' AND SS = 'C' AND FT.DEL = 'N' AND PR_S IS NULL AND EN_S IS NULL
AND (DC.ID IS NULL OR DC.SIGNED IS NULL);
It's relatively easy to do it with UPDATE:
UPDATE FT_T FTX
SET FTX.SS = 'X'
WHERE FT.GID = 'AB' AND SS = 'C' AND FT.DEL = 'N'
AND PR_S IS NULL AND EN_S IS NULL
AND (FTX.ID, FTX.CN) = (
SELECT FT.ID, FT.CN FROM FT_T FT LEFT OUTER JOIN DC_T DC
ON FT.ID = DC.ID AND FT.CN = DC.CN
WHERE FT.GID = 'AB' AND SS = 'C' AND FT.DEL = 'N' AND PR_S IS NULL AND EN_S IS NULL
AND (DC.ID IS NULL OR DC.SIGNED IS NULL)
)
But can it be done with MERGE?
Your merge statement would look like:
MERGE INTO ft_t tgt
USING (SELECT ft.id,
ft.cn
FROM ft_t ft
LEFT OUTER JOIN dc_t dc ON ft.id = dc.id AND ft.cn = dc.cn
WHERE ft.gid = 'AB'
AND ft.ss = 'C'
AND ft.del = 'N'
AND ft.pr_s IS NULL
AND ft.en_s IS NULL
AND (dc.id IS NULL OR dc.signed IS NULL)) src
ON (tgt.id = src.id AND tgt.cn = src.cn) -- assuming these two columns are the primary key for the ft_t table
WHEN MATCHED THEN
UPDATE SET tgt.ss = 'X';
I inherited a query that uses left joins. One of the things the query is doing is removing any archived records, that is where archived = 'Y'. This is how the query looks:
select P.firstname, E.entityid, C.committeeid, L.locationname
from Pract P
left join Committee C
on P.commiteeid = C.committeeid
and c.archived = 'N'
left join Entity E
on P.entityid = E.EntityID
and e.archived = 'N'
left join Location L
on E.location = L.location
and l.archived = 'N'
The result should only return records where archived <> 'Y'. I think a problem with putting the filter with the "on" is that it will return a record where c.archived = 'N' and just put a null in the archived field, which is not correct:
FirstName EntityID CommitteeId
John 55 null
If c.archived = 'Y' then the record should not show up.
I believe the archived filter should be in the where clause, like this:
select firstname, entityid, committeeid
from Pract P
left join Committee C
on P.commiteeid = C.committeeid
left join Entity E
on P.entityid = E.entityid
left join Location L
on E.Locationid = L.locationid
where c.archived = 'N'
and e.archived = 'N'
and l.archived = 'N'
The problem I'm finding is that there are instances where the archived field from Committee is null(it's not a 'Y' or an 'N'). Using my solution incorrectly eliminates the records since null <> 'N.'
If I try this:
where c.archived <> 'Y'
it does not work, I'm guessing because NULL does not evaluate to anything.
If I try this:
where (c.archived = 'N' or c.archived is null)
it doesn't work as it now brings back those null records caused by the left join. I can't replace the left join with an inner join because that will exclude records where c.committeeid is null.
I just want to bring back records where archived <> 'Y', which includes those where the field is null.
To be clear, this is what the records in the table can look like:
FirstName EntityID Archived
John 55 Y
Tom 56 NULL
Rob 57 N
In this instance I want the returned records to look like:
Tom 56 NULL
Rob 57 N
John would be eliminated because Archived = 'Y.'
Is there another way to do this?
If I understand correctly, you want to use another field in the where clause for the filtering:
select firstname, entityid, committeeid -- this will return an error on committeeid
from Pract P left join
Committee C
on P.commiteeid = C.committeeid and
(c.archived <> 'Y' or c.archived is null)
where c.committeeid is not null;
You may also be able to do this with exists more easily:
select p.*
from Pract P
where not exists (select 1
from Committee C
where P.commiteeid = C.committeeid and
c.archived = 'Y'
);
This handles the NULL values automagically.
I believe that you are looking for this version. You just start with what you had and modify joining condition to cover values that are not equal to Y and that are NULL.
select firstname, entityid, committeeid
from Pract P
left join Committee C
on P.commiteeid = C.committeeid
and (c.archived <> 'Y' OR c.archived IS NULL)
Then if you need only items that has record in commitee table. you put it in WHERE clause.
WHERE P.commiteeeid = C.commiteeid
But it will just emulate INNER JOIN.
Your problem with comparing NULL is that you can't use <> to test for NULL values. If you want records with NULL values you have to test for IS NULL explicitly.
So your updated query would look like:
select P.firstname, E.entityid, C.committeeid, L.locationname
from Pract P
left join Committee C
on P.commiteeid = C.committeeid
and (c.archived <> 'Y' OR c.archived IS NULL)
left join Entity E
on P.entityid = E.EntityID
and (e.archived <> 'Y' OR e.archived IS NULL)
left join Location L
on E.location = L.location
and (l.archived <> 'Y' OR l.archived IS NULL)
And if you have only values 'Y' 'N' and NULL, then you can also use
and (c.archived = 'N' OR c.archived IS NULL)
Just to be sure we understand each other here is minimal example for your query
create table Pract (firstname varchar(20), cid int)
create table comt ( cid int, archived varchar(1) )
insert into Pract values ('Tom',1),('Adam',2),('Mark',3),('Bob',4)
insert into comt values (1,'Y'), (2,'N'), (3,NULL)
--
select firstname, P.cid, C.cid, C.archived
from Pract P
LEFT join comt C
on P.cid = C.cid
and (c.archived <> 'Y' OR c.archived IS NULL)
With result
firstname cid cid archived
1 Tom 1 NULL NULL
2 Adam 2 2 N
3 Mark 3 3 NULL
4 Bob 4 NULL NULL
Or with INNER JOIN
select firstname, P.cid, C.cid, C.archived
from Pract P
INNER join comt C
on P.cid = C.cid
and (c.archived <> 'Y' OR c.archived IS NULL)
With result
firstname cid cid archived
1 Adam 2 2 N
2 Mark 3 3 NULL
The solution depends on how you want to handle the NULL values from the left join. If NULL means not archived then
select firstname, entityid, committeeid
from Pract P
left join Committee C
on P.commiteeid = C.committeeid
left join Entity E
on P.entityid = E.entityid
left join Location L
on E.Locationid = L.locationid
where (c.archived = 'N' OR c.archived IS null)
and (e.archived = 'N' OR e.archived IS null)
and (l.archived = 'N' OR l.archived IS NULL)