Adding new column with multiple values per existing record - sql

I'm interested in adding a column to an existing table with a set of explicit values that should duplicate existing records (similar to common join constructs).
For example, say we're starting with a table with a single column:
CREATE TABLE #DEMO (
COLUMN_A NVARCHAR(100) NOT NULL
PRIMARY KEY (COLUMN_A)
);
COLUMN_A
ACCOUNT_001
ACCOUNT_002
ACCOUNT_003
...and I want to add Column_B with row values of 'A', 'B', and 'C'. The end goal would be a table that looks like:
COLUMN_A
COLUMN_B
ACCOUNT_001
A
ACCOUNT_001
B
ACCOUNT_001
C
ACCOUNT_002
A
ACCOUNT_002
B
ACCOUNT_002
C
ACCOUNT_003
A
ACCOUNT_003
B
ACCOUNT_003
C
Is this possible? Bonus Points if there is a name or phrase for this you know of.

So I think you need couple of steps to first insert new rows and then update existing:
alter table #demo add COLUMN_B char(1);
with x as (
select * from (values('A'),('B'))x(B)
)
insert into #demo(COLUMN_A, COLUMN_B)
select COLUMN_A, B
from #DEMO cross join x
update #DEMO set COLUMN_B = 'C'
where COLUMN_B is null
Demo Fiddle

Related

Postgresql update column based on set of values from another table

Dummy data to illustrate my problem:
create table table1 (category_id int,unit varchar,is_valid bool);
insert into table1 (category_id, unit, is_valid)
VALUES (1, 'a', true), (2, 'z', true);
create table table2 (category_id int,unit varchar);
insert into table2 (category_id, unit)
values(1, 'a'),(1, 'b'),(1, 'c'),(2, 'd'),(2, 'e');
So the data looks like:
Table 1:
category_id
unit
is_valid
1
a
true
2
z
true
Table 2:
category_id
unit
1
a
1
b
1
c
2
d
2
e
I want to update the is_valid column in Table 1, if the category_id/unit combination from Table 1 doesn't match any of the rows in Table 2. For example, the first row in Table 1 is valid, since (1, a) is in Table 2. However, the second row in Table 1 is not valid, since (2, z) is not in Table 2.
How can I update the column using postgresql? I tried a few different where clauses of the form
UPDATE table1 SET is_valid = false WHERE...
but I cannot get a WHERE clause that works how I want.
You can just set the value of is_valid the the result of a ` where exists (select ...). See Demo.
update table1 t1
set is_valid = exists (select null
from table2 t2
where (t2.category_id, t2.unit) = (t1.category_id, t1.unit)
);
NOTES:
Advantage: Query correctly sets the is_valid column regardless of the current value and is a vary simple query.
Disadvantage: Query sets the value of is_valid for every row in the table; even thoes already correctly set.
You need to decide whether the disadvantage out ways the advantage. If so then the same basic technique in a much more complicated query:
with to_valid (category_id, unit, is_valid) as
(select category_id
, unit
, exists (select null
from table2 t2
where (t2.category_id, t2.unit) = (t1.category_id, t1.unit)
)
from table1 t1
)
update table1 tu
set is_valid = to_valid.is_valid
from to_valid
where (tu.category_id, tu.unit) = (to_valid.category_id, to_valid.unit)
and tu.is_valid is distinct from to_valid.is_valid;

How to update each row of a column from one table with a list of values from another?

I have a table A with records with 1 column only that are random alphanumerical characters. That table has say 10 rows.
I have another table B with 10 rows also that I want to take a value from a row from table A and apply it to a row in table B.
So basically, take a value from Table A and assign it to a row in table B. Preferably, take the value from table A row 1 and assign it to table B row 1, etc...
I am using SQL Server.
We can take any value from table B to assign to a row in table A. We just can't re-use a value from table B.
Here are the 2 tables in it's simplest form for this example:
CREATE TableA ([Value] NVARCHAR(50))
CREATE TableB ([Value] NVARCHAR(50))
Given the following table structure:
CREATE TABLE #tempA (stringEntry NVARCHAR(50));
INSERT INTO #tempA (stringEntry) VALUES ('abcd'), ('efgh'), ('ijkl');
CREATE TABLE #tempB (stringEntry NVARCHAR(50));
INSERT INTO #tempB (stringEntry) VALUES ('mnop'), ('qrst'), ('uvwx');
You can do the following:
SELECT
ROW_NUMBER() OVER(ORDER BY #tempA.stringEntry) AS RowNumber,
#tempA.stringEntry AS entryA
INTO #tempA2
FROM #tempA;
SELECT
ROW_NUMBER() OVER(ORDER BY #tempB.stringEntry) AS RowNumber,
#tempB.stringEntry AS entryB
INTO #tempB2
FROM #tempB;
UPDATE #tempA
SET #tempA.stringEntry = #tempB2.entryB
FROM #tempA
INNER JOIN #tempA2 ON #tempA.stringEntry = #tempA2.entryA
INNER JOIN #tempB2 ON #tempB2.RowNumber = #tempA2.RowNumber;
This assumes that you have equal number of rows in each table, as you indicated, or are okay with having the "excess" entries in your first table not being updated.

Searching multiple patterns with single LIKE clause

How could I achieve something like
SELECT *
FROM Table_1
WHERE Column_A CONTAINS ("AB" OR "BE" OR "DE");
I have more than 15 values to put in the contains() so it would be tedious to do it by hand (I mean "where A contains "AB" OR A contains "BE" etc.)
Thanks for any tips,
Try this:
SELECT *
FROM Table_1
WHERE Column_A in ('AB','BE','DE');
This will output all rows in Table_1 where Column_A have any value specified in IN CLAUSE. So it basically works as OR.
Note: This will work if you are searching 'AB' as a whole. If you want all rows having 'AB' or 'BE' anywhere in the column value string then you have to use like clause like this:
SELECT *
FROM Table_1
WHERE Column_A like '%AB%' OR Column_A like '%BE%' OR Column_A like '%DE%';
Update : You could use below one as well, this serves your purpose and you don't have to write LIKE Clause 15-20 times.
Create table employee (name varchar(200))
Insert into employee values('ABC')
Insert into employee values('DEF')
Insert into employee values('BCD')
Insert into employee values('EFG')
Insert into employee values('ADC')
SELECT
distinct name
FROM
(
VALUES
('%A%'),
('%D%')
) AS v (pattern)
CROSS APPLY
( -- your query
SELECT *
FROM employee
WHERE name like v.pattern
) AS x

Detect differences between two versions of the same table

I am looking for a method to detect differences between two versions of the same table.
Let's say I create copies of a live table at two different days:
Day 1:
CREATE TABLE table_1 AS SELECT * FROM table
Day 2:
CREATE TABLE table_2 AS SELECT * FROM table
The method should identify all rows added, deleted or updated between day 1 and day 2;
if possible the method should not use a RDBMS-specific feature;
Note: Exporting the content of the table to text files and comparing text files is fine, but I would like a SQL specific method.
Example:
create table table_1
(
col1 integer,
col2 char(10)
);
create table table_2
(
col1 integer,
col2 char(10)
);
insert into table_1 values ( 1, 'One' );
insert into table_1 values ( 2, 'Two' );
insert into table_1 values ( 3, 'Three' );
insert into table_2 values ( 1, 'One' );
insert into table_2 values ( 2, 'TWO' );
insert into table_2 values ( 4, 'Four' );
Differences between table_1 and table_2:
Added: Row ( 4, 'Four' )
Deleted: Row ( 3, 'Three' )
Updated: Row ( 2, 'Two' ) updated to ( 2, 'TWO' )
If you want differences in both directions. I am assuming you have an id, because you mention "updates" and you need a way to identify the same row. Here is a union all approach:
select t.id,
(case when sum(case when which = 't2' then 1 else 0 end) = 0
then 'InTable1-only'
when sum(case when which = 't1' then 1 else 0 end) = 0
then 'InTable2-only'
when max(col1) <> min(col1) or max(col2) = min(col2) or . . .
then 'Different'
else 'Same'
end)
from ((select 'table1' as which, t1.*
from table_1 t1
) union all
(select 'table2', t2.*
from table_2 t2
)
) t;
This is standard SQL. You can filter out the "same" records if you want to.
This assumes that all the columns have non-NULL values and that rows with a given id appear at most once in each table.
I think I found the answer - one can use this SQL statement to build a list of differences:
Note: "col1, col2" list must include all columns in the table
SELECT
MIN(table_name) as table_name, col1, col2
FROM
(
SELECT
'Table_1' as table_name, col1, col2
FROM Table_1 A
UNION ALL
SELECT
'Table_2' as table_name, col1, col2
FROM Table_2 B
)
tmp
GROUP BY col1, col2
HAVING COUNT(*) = 1
+------------+------+------------+
| table_name | col1 | col2 |
+------------+------+------------+
| Table_2 | 2 | TWO |
| Table_1 | 2 | Two |
| Table_1 | 3 | Three |
| Table_2 | 4 | Four |
+------------+------+------------+
In the example quoted in the question,
Row ( 4, 'Four' ) present in table_2 ; verdict row "Added"
Row ( 3, 'Three' ) present in table_1; verdict row "Deleted"
Row ( 2, 'Two' ) present in table_1 only; Row ( 2, 'TWO' ) present in table_2 only; if col1 is primary key then verdict "Updated"
If you can asasume that the table has a unique primary key then the following SQL statements will at the end have created three views which contain the changed, new and deleted IDs.
In the following "tbl1" is the old version and "tbl2" is the new version (same schema). The primary key in the table is assumed to be named "_id".
The intermediate view "_NewChanged" will contain both the new and changed IDs in the new version of the table and the view "_RemovedChanged" will contain both the removed and changed IDs in the new version.
To generate delta SQL statements is just a matter of programatically looping the respective views to create a delta set and do the delete, update, insert statemens that transforms the old version to the new version.
The solution have three caveats:
It will not pin-point exactly what columns have changed
The schema is assumed to be the same in the old and new version of the table
It assumes that the SQL statments INTERSECT and EXCEPT are available
To keep the code brief no comments have been inserted.
drop view if exists _NewChanged;
drop view if exists _RemovedChanged;
create view _NewChanged as
select * from tbl2
except
select * from tbl1;
create view _RemovedChanged as
select * from tbl1
except
select * from tbl2;
drop view if exists changed;
drop view if exists new;
drop view if exists deleted;
create view changed as
select _id from _NewChanged
intersect
select _id from _RemovedChanged;
create view new as
select _id from _NewChanged
except
select _id from _RemovedChanged;
create view deleted as
select _id from _RemovedChanged
except
select _id from _NewChanged;

HSQLDB LIKE query fails with sharp-s

I'm unable to write a working LIKE query for a field containing the German sharp-s (ß) in a case-insensitive text field.
Using HSQLDB 2.2.9, create a table with a case sensitive field and a case insensitive field.
CREATE CACHED TABLE MYTABLE (MYKEY LONGVARCHAR NOT NULL, PRIMARY KEY (MYKEY));
ALTER TABLE MYTABLE ADD COLUMN SEN LONGVARCHAR;
ALTER TABLE MYTABLE ADD COLUMN INSEN VARCHAR_IGNORECASE;
Write 2 records.
INSERT INTO MYTABLE (MYKEY, SEN, INSEN) VALUES ('1', 'Strauß', 'Strauß');
INSERT INTO MYTABLE (MYKEY, SEN, INSEN) VALUES ('2', 'Strauss', 'Strauss');
Verify.
SELECT * FROM MYTABLE
KEY, SEN, INSEN
'1', 'Strauß', 'Strauß'
'2', 'Strauss', 'Strauss'
The problem query:
SELECT * FROM MYTABLE WHERE INSEN LIKE '%ß%'
WRONG, RETURNS RECORD 2 NOT RECORD 1
These queries work as expected:
SELECT * FROM MYTABLE WHERE SEN LIKE '%ß%'
OK, RETURNS RECORD 1
SELECT * FROM MYTABLE WHERE UCASE(INSEN) LIKE '%ß%'
OK, RETURNS RECORDS 1 AND 2
SELECT * FROM MYTABLE WHERE UCASE(SEN) LIKE '%ß%'
OK, RETURNS NOTHING
SELECT * FROM MYTABLE WHERE SEN='Strauß'
OK, RETURNS RECORD 1
SELECT * FROM MYTABLE WHERE INSEN='Strauß'
OK, RETURNS RECORD 1
SELECT * FROM MYTABLE WHERE SEN='Strauss'
OK, RETURNS RECORD 2
SELECT * FROM MYTABLE WHERE INSEN='Strauss'
OK, RETURNS RECORD 2
Thanks!