UPDATE FROM in SQL with matching key - sql

How can I UPDATE FROM in SQL? I want to copy the FamilyID from my FamilyGuardian table:
dbo.FamilyGuardian
PupilID FamilyID GuardianID
---------------------------------
1 100002 555
2 100003 556
3 100004 557
4 100005 558
5 100006 559
6 100007 1146
7 100008 561
8 100009 562
9 100010 563
10 100011 564
Into the Guardians table where GuardianID is equal.
dbo.Guardians
GuardianID Name FamilyID
---------------------------------
555 Smith NULL
556 Patel NULL
557 Andrews NULL
558 Brown NULL
559 Abdul NULL
dbo.Guardians after INSERT FROM
GuardianID Name FamilyID
--------------------------------
555 Smith 100002
556 Patel 100003
557 Andrews 100004
558 Brown 100005
559 Abdul 100006

you have to use an UPDATE query
UPDATE T1
SET T1.FamiltyID = T2.FamilyID
FROM dbo.Guardians AS T1 INNER JOIN dbo.FamilyGuardian AS T2
ON T1.GuardianID = T2.GuardianID

There is no such thing as INSERT FROM - you're looking for UPDATE, not INSERT:
Update G
Set FamilyId = F.FamilyId
From FamilyGuardian F
Join Guardian G On G.GuardianId = F.GuardianId

You might use an updateable CTE for this
WITH UpdateableCTE AS
(
SELECT g.FamilyID
,fg.FamilyID AS newValue
FROM dbo.Guardians AS g
INNER JOIN dbo.FamilyGuardian AS fg ON g.GuardianID=fg.GuardianID
)
UPDATE UpdateableCTE SET FamilyID=newValue;
But you must be aware, that this design is - uhm - weak...
What would happen if one Guard is responsible for more than one family? What happens, if the information on one side changes, but the other side does not change?
I think, this should not get into a physically defined column at all. This should be taken with a JOIN whenever you read this data.

you mean an UPDATE, not an INSERT, right?
In that case you should use an structue like:
UPDATE dbo.Guardians
SET FamilyID = (SELECT DISTINCT FamilyID FROM dbo.FamilyGuardian fg WHERE fg.GuardianID = GuardianID )
Notice that the DISTINCT is only necessary when there are more than 1 familyId per guardianId.

Related

Merge rows by adding the deviating value as new columns

I want to output for each person that has filled out a specific questionnaire their personal data as well as the data from the specific questionnaire/assessment. The values are stored in the table criteriaDF.
The result of my current query:
KlientId
Name1
Name2
CriteriaName
Result
335
Name1Person1
Name2Person1
IF1
Yes
335
Name1Person1
Name2Person1
IF2
Yes
335
Name1Person1
Name2Person1
IF3
No
336
Name1Person2
Name2Person2
IF1
Yes
336
Name1Person2
Name2Person2
IF2
Yes
336
Name1Person2
Name2Person2
IF3
No
What I want to have:
KlientId
Name1
Name2
IF1
IF2
IF3
335
Person1Name1
Person1Name2
Yes
Yes
No
336
Person2Name1
Person2Name2
Yes
No
No
So the criterias should get their own columns and the rows referencing the same person should merge into one based on the same KlientId.
The query I used to get my current result uses a few joins in order to get from person to client, to process to assessment to criteria, where the CriteriaName and Result lies. The other tables are just used with their foreign keys to get to these values "IF1: Yes" etc.
SELECT Client.Id, Person.Name1, Person.Name2, Person.Birthday, CriteriaDF.CriteriaName, CriteriaDF.Result
FROM Client
INNER JOIN Person ON Client.PersonId=Person.Id
INNER JOIN Process ON Client.Id=Process.ClientId
INNER JOIN AssessmentDF ON Process.Id=AssessmentDF.ProCessId
INNER JOIN CriteriaDF ON AssessmentDF.Id=CriteriaDF.AssessmentDfId
WHERE AssessmentDF.Name='RightAssessmentName' AND AssessmentDF.Date > DATEADD(day, -180, GETDATE())
Edit: Query using aliases:
SELECT t1.Id, t2.Name1, t2.Name2, t2.Birthday, t5.CriteriaName, t5.Result
FROM Client AS t1
INNER JOIN Person AS t2 ON t1.PersonId=t2.Id
INNER JOIN Process AS t3 ON t1.Id=t3.ClientId
INNER JOIN AssessmentDF AS t4 ON t3.Id=t4.ProcessId
INNER JOIN CriteriaDF AS t5 ON t4.Id=t5.AssessmentDfId
WHERE t4.Bezeichnung='RightAssessmentName' AND t4.Date > DATEADD(day, -180, GETDATE())
My main question is how to convert the tuples CriteriaName & Result to a new column for each unique CriteriaName and Fill the cell with the Result.
I dont think they're important, but here are all minimal tables that are used to get from the person to the criterias (Ids might not fit perfectly to the result & what I wanted to have, just to understand how the data is stored):
Table Person:
Id
Name1
Name2
2766
Person1Name1
Person1Name2
2767
Person2Name2
Person2Name2
2768
Person3Name2
Person3Name2
Table Klient:
Id
PersonId
1
2766
335
2767
336
2768
Table Process:
Id
KlientId
2485
335
2515
336
2519
336
Table AssessmentDF
Id
ProcessId
Date
Name
43
2485
2022-04-18
RightAssessmentName
44
2515
2022-05-18
RightAssessmentName
45
2519
2022-06-18
RightAssessmentName
Table CriteriaDF:
In reality there is IF1-If19
Id
AssessmentDfId
CriteriaName
ProcessId
Result
551
43
IF1
2485
Yes
552
43
IF2
2485
Yes
553
43
IF3
2485
No
554
44
IF1
2515
Yes
555
44
IF2
2515
Yes
556
44
IF3
2515
No
557
45
IF1
2519
Yes
558
45
IF2
2519
No
559
45
IF3
2519
No

Join multiple tables and pick results from most recent table

I have 4 tables. I want all the rows and cols from my first table tbl_2021 and only those data which are not in tbl_2021 but present in the the rest 3 tables, but based on one condition
if there id exist in tbl_2020, tbl_2019 and in tbl_2018 then i need the id and it's details from the most recent table that is tbl_2020.
if an id is across 2019 and 2018 table, then i need the data from 2019 so on like that.If in 2020 and 018 then 020 and so on
if the same is across 2021,2020,2019 and 2018 then the data from 2021 is selected.
And - I'm hail from a shell scripting background, and i've just started with sql. so if any noble mind could tell me the approach or what i should do to get these pieces together would mean more than happiness to me. Thank you
tbl_2021
id
name
addr
location
country
contintent
gdp
123
rob
dware
texas
us
us
8
456
lilly
gwood
london
uk
uk
5
670
rick
utown
newyrok
us
us
8
490
zang
kcity
hk
hongkong
hongkong
6
tbl_020
id
location
name
999
ger
roger
888
bel
leslie
670
us
marie
tbl_019
id
location
name
data
network
999
uk
roger
xx
na
555
rus
vladmir
ux
na
879
us
marie
xx
ua
481
cn
kim
tbl_018
id
location
name
data
network
823
uk
roger
xx
na
555
rus
vladmir
ux
na
879
us
maria
xx
ua
670
us
marie
xy
uy
888
in
raj
xx
jo
output:
id
name
addr
location
country
contintent
gdp
123
rob
dware
texas
us
us
8
456
lilly
gwood
london
uk
uk
5
670
rick
utown
newyrok
us
us
8
490
zang
kcity
hk
hongkong
hongkong
6
999
roger
ger
888
leslie
bel
555
vladmir
rus
879
marie
us
481
kim
cn
823
roger
uk
First, you should fix your data model. It is not a good idea to store such data in separate tables. Instead, you should store in a single table with a year column.
Second, I think you can solve your problem using full join, but it is a little tricky:
select coalesce(t21.id, t20.id, t19.id, t18.id) as id,
coalesce(t21.name, t20.name, t19.name, t18.name) as name,
t21.addr,
. . .
from tbl_2021 t21 full join
tbl_2020 t20
on t21.id = t20.id full join
tbl_2019 t19
on t19.id = coalesce(t21.id, t20.id) full join
tbl_2018 t18
on t18.id = coalesce(t21.id, t20.id, t19.id);
You need to carefully figure out how the columns should be pulled from the different tables.
First you can union all the data from four tables with union all. Then with row_number() we need to serialized rows for each id from higher to lower. Finally select one row for each id with highest year .
with cte as
(
select id,name addr ,location ,country, contintent,data,network, row_number()over (partition by id order by sl ) rn from
(
select id,name ,addr ,location , country, contintent,data,network, 1 sl from tbl_21
union all
select id,name ,'' addr ,location ,'' country,'' contintent, data, network, 2 sl from tbl_20
union all
select id,name ,'' addr ,location ,'' country,'' contintent, data,network, 3 sl from tbl_19
union all
select id,name ,'' addr ,location ,'' country,'' contintent, data,network, 4 sl from tbl_18
)t
)
select id,name ,addr ,location ,country, contintent,data,network from cte where rn=1

Exclude rows where keys match, but are on different rows

I'm looking for the best way to produce the result set in the scenario provided. My cust3 column isn't identifying the repeated values in the indvid2 column. The end result I'm looking for is to exclude the rows where key1 and key2 match (ids:1,2,6 and 7), then sum accounts where the acctids match.If there's a better way to code this, I welcome all suggestions. Thanks!
WITH T10 as (
SELECT acctid,invid,(
case
when invid like '%-R' then left (InvID,LEN(invid) -2) else InvID
END) as InvID2
FROM table x
GROUP BY acctID,invID
),
T11 as (
SELECT acctid, Invid2, COUNT(InvID2) as cust3
FROM T10
GROUP BY InvID2,acctid
HAVING
COUNT (InvID2) > 1
)
select DISTINCT
a.acctid,
a.name,
b.invid,
C.invid2,
D.cust3,
b.amt,
b.key1,
b.key2
from table a
inner join table b (nolock) on a.acctid = b.acctid
inner join T10 C (nolock) on b.invid = c.invid
inner join T11 D (nolock) on C.invid2 = D.invid2
Resultset
id acctID name invid invid2 Cust3 amt key1 key2
1 123 James 101 101 2 $500 NULL 6789
2 123 james 101-R 101 2 ($500) 6789 NULL
3 123 James 102 102 2 $350 NULL NULL
4 123 James 103 103 2 $200 NULL NULL
5 246 Tony 98-R 98 2 ($750) 7423 NULL
6 432 David 45 45 2 $100 NULL 9634
7 432 David 45-R 45 2 ($100) 9634 NULL
8 359 Stan 39-R 39 2 ($50) 6157 NULL
9 753 George 95 95 2 $365 NULL NULL
10 753 George 108 108 2 $100 NULL NULL
Desired Resultset
id acctID name invid invid2 Cust3 amt key1 key2
1 123 James 101 101 2 $500 NULL 6789
2 123 james 101-R 101 2 ($500) 6789 NULL
3 123 James 102 102 1 $350 NULL NULL
4 123 James 103 103 1 $200 NULL NULL
5 246 Tony 98-R 98 1 ($750) 7423 NULL
6 432 David 45 45 2 $100 NULL 9634
7 432 David 45-R 45 2 ($100) 9634 NULL
8 359 Stan 39-R 39 1 ($50) 6157 NULL
9 753 George 95 95 1 $365 NULL NULL
10 753 George 108 108 1 $100 NULL NULL
Then to sum amt by acctid
id acctid name amt
1 123 James $550
2 246 Tony ($750)
3 359 Stan ($50)
4 753 George $465
Something like:
;WITH Keys as (
SELECT Key1.acctID, [Key] = Key1.Key1
FROM YourTable as Key1
INNER JOIN YourTable as Key2
ON Key1.Key1 = Key2.Key2 and Key1.acctID = Key2.acctID
)
SELECT t.acctID, t.name, amt = SUM(t.amt)
FROM YourTable as t
LEFT JOIN Keys as k
ON t.acctID = k.acctID and (t.Key1 = [Key] or t.Key2 = [Key])
WHERE k.acctID is Null
GROUP BY t.acctID, t.name

How I select record that not appear in another table

Table: Movie
mID title year director
101 Gone with the Wind 1939 Victor Fleming
102 Star Wars 1977 George Lucas
103 The Sound of Music 1965 Robert Wise
104 E.T. 1982 Steven Spielberg
105 Titanic 1997 James Cameron
106 Snow White 1937 <null>
107 Avatar 2009 James Cameron
108 Raiders of the Lost Ark 1981 Steven Spielberg
Table: Rating
rID mID stars ratingDate
201 101 2 2011-01-22
201 101 4 2011-01-27
202 106 4 <null>
203 103 2 2011-01-20
203 108 4 2011-01-12
203 108 2 2011-01-30
204 101 3 2011-01-09
205 103 3 2011-01-27
205 104 2 2011-01-22
205 108 4 <null>
206 107 3 2011-01-15
206 106 5 2011-01-19
207 107 5 2011-01-20
208 104 3 2011-01-02
I need to fetch movies which are not rate yet. In this case Titanic (mID 105) and Star Wars (mID 102) never get rate in rating table.
I figured out it with
select distinct movie.title from movie,rating where
rating.mid!=movie.mid except select distinct movie.title from
movie,rating where rating.mid=movie.mid
however I think it might have better (easier/cleaner) way to do.
Simple:
SELECT Movies.* FROM Movies LEFT JOIN Rating ON Movies.mID = Rating.mID WHERE Rating.mID IS NULL
If I understood your question properly, that looks like textbook application of outer joins.
You could do it like this:
SELECT * FROM Movie WHERE mid NOT IN (SELECT DISTINCT(mid) FROM Rating)
Basically it will select all records from the movie table that are not in the rating table, linking them on the 'mid' column, which I am assuming is a unique identifier.
I will add another possibility.
Select [list columns here]
from Movie m
where NOT exists (SELECT * FROM RATING r where m.mid = r.mid)

SQL Query pivot approach assistance

i am really struggling with this pivot and hoped reaching out for help and enlightenment might help.
Say i have the following table....
Table A
type actId date rowSort order value value_char colName
------------------------------------------------------------------------------------
checking 1003 2011-12-31 2 1 44 44 Amount
checking 1003 2011-12-31 2 2 55 55 Interest
checking 1003 2011-12-31 2 3 66 66 Change
checking 1003 2011-12-31 2 4 77 77 Target
checking 1003 2011-12-31 2 5 88 88 Spread
savings 23456 2011-12-31 1 1 999 999 Amount
savings 23456 2011-12-31 1 2 888 888 Interest
savings 23456 2011-12-31 1 3 777 777 Change
savings 23456 2011-12-31 1 4 666 666 Target
savings 23456 2011-12-31 1 5 555 555 Spread
And i want to transpose to table b
checking chkId date rowSort order chkvalue chkValchar colName savings savId savVal savValChar
-------------------------------------------------------------------------------------------------------------------
checking 1003 2011-12-31 2 1 44 44 Amount savings 23456 999 999
checking 1003 2011-12-31 2 2 55 55 Interest savings 23456 888 888
checking 1003 2011-12-31 2 3 66 66 Change savings 23456 777 777
checking 1003 2011-12-31 2 4 77 77 Target savings 23456 666 666
checking 1003 2011-12-31 2 5 88 88 Spread savings 23456 555 555
I can admit this is beyond my skills at the moment.
I believe i need to do a pivot on this table, using the rowSort (identify savings vs checking) along with ordering using the order column. This maybe wrong and that is why i am here.
Is a pivot the right way to go? Am i right to assume my pivot is to use the aggregate max(rowSort)?
Assuming rowSort from `checking equal to rowSort+1 from savings and the rows link though field value, this should do it:
SELECT DISTINCT
a.type as checking,
a.actId as chkId,
a.date,
a.rowSort+1,
a.order,
a.value as chkvalue,
a.value_char as chkValchar,
a.colName,
b.type as 'savings',
a.actId as savId,
b.value as savVal,
b.value_char as savValChar
FROM tablea a
INNER JOIN tablea b ON b.rowSort = a.rowSort+1 and b.value = a.value
Based on the requirements you presented, you will not use a PIVOT for this query, you will want to JOIN your table to itself. The query below should give you the records that you want without having to use a DISTINCT
select c.type as checking
, c.actId as chkid
, c.date
, c.rowsort
, c.[order]
, c.value as chkvalue
, c.value_char as chkValchar
, c.colName
, s.type as savings
, s.actId as savId
, s.value as savVal
, s.value_char as savValchar
from t1 c
inner join t1 s
on c.rowsort = s.rowsort + 1
and c.[order] = s.[order]
See SQL Fiddle with Demo