Update column with another column value of different row in same table - sql

I have a table where I am storing properties for entries.
Id - Foreign key. Points out to the actual entry in another table.
Key - The key of the property.
Value - The value of the property.
Current table structure:
id
Key
Value
1
property1
value11
1
oldvalue
oValue1
1
newvalue
nValue1
2
property1
value12
2
oldvalue
oValue2
2
newvalue
nValue2
3
property1
value13
3
oldvalue
oValue3
3
newvalue
nValue3
I newly added the column Previous Value and I want to store properties' old value and new value in a single row instead of two. Thus, I will need to copy the value of the "oldvalue" key into the new column of same id, and rename the "newvalue" key to just "value". Also this needs to be done iteratively for all the id's where the oldvalue property-value is present.
Target table structure:
id
Key
Value
Previous Value
1
property1
value11
null
1
value
nValue1
oValue1
2
property1
value12
null
2
value
nValue2
oValue2
3
property1
value13
null
3
value
nValue3
oValue3
So basically, for each ID, if the key-value for "oldvalue" key is present, then copy the value of the property "oldvalue" to Previous Value column of the key "newvalue" for the same ID. Then rename all "newvalue" key to the new "value" only.
The above target table structure is what I need at the end after adding the column "Previous Value".
I have written an SQL Query for SQL Server and it is working:
update t1 set t1.previous_value = t2.value
from PROPERTIES_TABLE t1,
PROPERTIES_TABLE t2
where t1.id = t2.id
AND t1.key = 'newvalue'
and t2.key = 'oldvalue';
update PROPERTIES_TABLE
set key = 'value'
where key = 'newvalue';
But the query is not working since aliases does not work in PostgreSQL.

In PostgreSQL the target table of an UPDATE should not be repeated in the FROM clause.
update PROPERTIES_TABLE t1
set t1.previous_value = t2.value
from PROPERTIES_TABLE t2
where t1.id = t2.id
AND t1.key = 'newvalue'
and t2.key = 'oldvalue';

Related

SQL (VBA/ADO) command to populate NULL fields with corresponding non-NULL values from duplicate records

I have a database where several hundred records have been duplicated. However the duplicated information is not the same across all fields. For any two lines, the first line will contain information in some fields while the duplicate line's fields are blank; but then for other fields, the duplicate (second) line will contain information while the first line's fields are blank. For example, it looks like this:
ID Deleted Reference Name Case_Date Outcome Outcome_Date
100 False A123 Chris 2000-01-01 Yes
101 False A123 Chris 2000-03-31
The ID column is a unique primary key for the record.
The Reference column is the one by which I can identify the duplicates.
However as you can see, the first record (100) contains information in Case_Date and Outcome, but the second record (101) contains an Outcome_Date.
What I want to do is to copy the most amount of information into just one of each pair of records, and then mark the duplicate as deleted (I use a soft-delete, not actually removing records from the table but just flagging the Duplicate column as True). With the above example, I want it to look like this:
ID Deleted Reference Name Case_Date Outcome Outcome_Date
100 False A123 Chris 2000-01-01 Yes 2000-03-31
101 True A123 Chris (2000-01-01)* (Yes)* 2000-03-31
Technically it will not be necessary to also copy information into the blank fields of the record which will be marked as deleted, but I figure it's easier to just copy everything and then mark the "second" record as the duplicate, rather than trying to work out which one contains more information and which one contains less.
I am also aware that it will be easier to run a separate SQL command for each column than to try to do them all at once. The columns shown above are a simplified example, and the information which may or may not be present across each column differs.
My select query for the record set of duplicates is:
SELECT *
FROM [Cohorts]
WHERE [Deleted] = False
AND ([CaseType] = "Female" OR [CaseType] = "Family")
AND [Reference] Is Not Null
And [Reference] In (SELECT [Reference] FROM [Cohorts] As Tmp
WHERE [Deleted] = False
AND ([CaseType] = "Female" OR [CaseType]="Family")
GROUP BY [Reference]
HAVING Count(*) > 1)
ORDER BY [Reference];
This will return all (Female/Family) records in the table [Cohorts] where there exists more than one record with the same Reference (and where the records have not been marked as deleted).
I'm running my queries from VBA via ADO, so can execute UPDATE statements. My database is an Access-compatible .mdb using the JET engine.
Grateful if anyone could suggest a suitable SQL command which I can run per column in order to populate the NULL fields with the values of the non-NULL fields from the relevant duplicate records. It's a bit beyond my SQL understanding at present! Thanks.
My first UPDATE JOIN ever, hope it works (untested):
update t1
set t1.name = coalesce(t1.name, t2.name),
t1.Case_Date = coalesce(t1.Case_Date, t2.Case_Date),
t1.Outcome = coalesce(t1.Outcome, t2.Outcome),
t1.Outcome_Date = coalesce(t1.Outcome_Date, t2.Outcome_Date),
t1.deleted = case when t1.id < t2.id then FALSE else TRUE end
from Cohorts t1
join Cohorts t2 on t1.Reference = t2.Reference and t1.id <> t2.id
Edit: Alternative solution:
Create a copy table, do insert select:
insert into CohortsCopy (Deleted, Reference, Name, Case_Date, Outcome, Outcome_Date)
select case when t1.id < t2.id or t2.id is null then FALSE else TRUE end,
coalesce(t1.Reference, t2.Reference),
coalesce(t1.name, t2.name),
coalesce(t1.Case_Date, t2.Case_Date),
coalesce(t1.Outcome, t2.Outcome),
coalesce(t1.Outcome_Date, t2.Outcome_Date)
from Cohorts t1
left join Cohorts t2 on t1.Reference = t2.Reference and t1.id <> t2.id
Then either rename, or copy back to original table.

Why row is updated wrongly in DB?

I am under a situation where I have to update the rows of two column and I am not able to find the correct solution.
I have table like this:
table1 :
nid listName ltitle
1 lsn1 lst1
2 lsn2 lst2
and now this nid is foreign key(listid) for table2
table2
nid listid listcol1 listcol2
1 1 "lstxt1" "lscol1" //belongs to lsn1
2 1 "lstxt2" "lscol2" //belongs to lsn1
3 1 "lstxt3" "lscol3" //belongs to lsn1
3 2 "lstxt4" "lscol4" //belongs to lsn2
For better understanding there are two list named lsn1 and lsn2 and first one has 2 columns listcol1 and listcol2 and listcol1 contains the data which come
through a string which I split through "###" as delimiter something like this "lstxt1###lstxt2###lstxt3###" similarly for listcol2
My try to update both table in stored prcodure on data changed by user is :
update table1 set listName=#listName ,ltitle=#ltitle //table 1 update and it works well
where nid=#nid
update table2 set listId=#nid, listcol1=a.part,listcol2= b.part from //table2 update , problem is here
dbo.splitstring(#listcol1,'###') a inner join
dbo.splitstring(#listcol2,'###') b on a.id=b.id where table2.listid=#nid
problem here is :
(1) table1 is updated properly but table2 updated output is wrong (lets say i tried to update for nid=1 in table1) :
listcol1 listcol2
"lstxt5" "lscol5" //belongs to lsn1
"lstxt5" "lscol5" //belongs to lsn1
"lstxt5" "lscol5"
I mean it updates the first row to all rows. Where as expected output was :
listcol1 listcol2
"lstxt5" "lscol5" //belongs to lsn1
"lstxt6" "lscol6" //belongs to lsn1
"lstxt7" "lscol7"
(2) when user add new row it do not show the update in that row. It must show taht as well.
This part of the code (as far as I understand your logic) for sure does not make any sense: listcol1=a.part,listcol1= b.part
That should give you an error like this:
Msg 264, Level 16, State 1, Line 3
The column name 'listcol1' is specified more than once in the SET clause or column list of an INSERT. A column cannot be assigned more than one value in the same clause. Modify the clause to make sure that a column is updated only once. If this statement updates or inserts columns into a view, column aliasing can conceal the duplication in your code."
update table1 set listName=#listName ,ltitle=#ltitle //table 1 update and it works well
where nid=#nid
update table2 set listId=#nid, **listcol1=a.part,listcol1= b.part** from //table2 update , problem is here
dbo.splitstring(#listcol1,'###') a inner join
dbo.splitstring(#listcol2,'###') b on a.id=b.id where table2.listid=#nid

Changing the values in a column with a value from the same column

Is it possible in access to select a record from a given column, and update that same column with the record you selected?
For example:
----column----
test
---------------
becomes
-----Column------
test
test
test
test
-------------------
Notice the blanks in the first table, and how in the second table those blanks were filled with the value that was in the first row. Is there a way to do this without having to specifically say "update to test"? I'm at a loss as to how to do this without telling Access that it needs to specifically update the blanks to "test".
This will do what you want:
UPDATE Table1, (SELECT TOP 1 Field1 As F FROM Table1 WHERE Field1 Is Not Null)
SET Field1 = F
WHERE Field1 Is Null
It handles several special cases safely:
If more than one row has a value in Fleld1, the value from the first such row is used.
The first row of the table need not have a value in Field1.
If none of the rows have a value in Field1, nothing bad will happen.
Before:
Field1 Field2
apple
pet cat
dog
color red
blue
After:
Field1 Field2
pet apple
pet cat
pet dog
color red
pet blue
Assuming you have:
a table MY_TABLE
a column COL_1
multiple records of which only one holds a value (not null) for COL_1
The update statement to fill COL_1 of all records with the non-null value :
update MY_TABLE
set COL_1 = (select COL_1 FROM MY_TABLE where COL_1 is not null)
where COL_1 IS NULL;

Compare "Firstname Lastname" with "F. Lastname" in SQL

I have two tables, Table1 and Table2.
Table1 consists of a column (FullName) of names in the format 'FirstName LastName' and a unique identifier (Key). Table2 has a column (Name) consisting of these names but in the form 'F. LastName'.
I would like to replace the Names in Table2 with the unique identifier (Key) from Table1. This requires a comparison of the strings in FullName and Name (this comparison is bijective).
Is this possible to do using SQL?
You can do the update directly, but you have problems code can't overcome.
create table table1 (
key integer primary key,
firstname varchar(20),
lastname varchar(20)
);
insert into "table1" values(1,'Ian','McCormick');
insert into "table1" values(2,'Irving','McCormick');
create table table2 (name varchar(30));
insert into table2 values('I. McCormick');
See the problem? Both 'Ian McCormick' and 'Irving McCormick' map to 'I. McCormick'. Which ID number should 'I. McCormick' end up with? There's no way to tell.
Anyway, update carefully. Back up your database. Then add a new column for the id number.
alter table Table2
add column key integer;
Now you can update the new column without risking the data in the old column.
update table2
set key = (select key
from table1
where name = substr(FirstName, 1, 1) || '. ' || LastName);
You can join on expressions, too. Joining on the expression you intend to use in an update is a good first step--it can alert you to problems before it's too late. Also, as below, it can show you the carnage that can result from bad data.
sqlite> select * from table1
...> inner join table2
...> on substr(FirstName, 1, 1) || '. ' || LastName = table2.name;
key firstname lastname name key
--
1 Ian McCormick I. McCormick 1
2 Irving McCormick I. McCormick 1
Notice the mismatch in keys on the second row.

SQL Server copy contents based on ID

In my DB I have 7 rows for 3 different entities, I want to mirror the contents of one entity to the other two based on an ID value. I do not know if an update statement is appropriate.
CoId DocumentType StatusId StatusDescription Default Text Progression Environment RequiredOnAssign TS DocumentFilterGroup
These are my column headers, CoId can have one of three values, 1, 2, or 3. I wish to copy the contents of 1 into 2 and 3 based on status ID. I have having trouble articulating further than this.
If I understand correctly, then a self-join is your best friend:
UPDATE t1
SET DocumentType = t1.DocumentType, StatusDescription = t1.StatusDescription, Default = t1.Default -- the same for the rest of the fields
FROM table t1
INNER JOIN table t2
ON t1.CoID in (2,3) and t2.CoID = 1
WHERE StatusID = ...