bulk update in SQLite - sqlite

I have 2 tables with identical structure I want to update one table using data from the other, matching on primary key. SQLite has a with (CTE) statement but the following doesn't work (sqlite3 v. 3.29.0):
sqlite> select * from main;
1|A
2|B
4|D
5|E
6|F
sqlite> select * from temp;
1|aa
2|bb
3|cc
4|dd
5|ee
sqlite> with mapping as (select main.ID, temp.Desc from main join temp on temp.ID=main.ID) update main set Desc=mapping.Desc where main.ID=mapping.ID;
Error: no such column: mapping.Desc
I've tried using "select main.ID as ID, temp.Desc as Desc", but get the same error message.

To update your main table from your cte, use a subquery, since sqlite doesn't support update from
with mapping as
(select main.ID, temp.Desc
from main
join temp on temp.ID=main.ID)
update main set Desc=
(select Desc from mapping where ID = main.ID limit 1);
see dbfiddle

Related

SQLite - Joining 3 different databases at a single one

I'm trying to Join tables from 3 different databases (A, B and C) which have columns in commom, but i got some issues. Each database has 1 table with the same name of the database.
First, I tried to do that using VSCode (sqlite3 extension) by attaching the databases B to A:
ATTACH 'A.db' AS db1;
ATTACH 'B.db' AS db2;
SELECT * FROM db1.A
INNER JOIN db2.B ON B.ColumnInCommon = A.ColumnInCommon;
I got the following error:
No such table: db1.A
Could anyone help me with another solution?
I believe that you have to believe what the message says. The following demonstrates that what you are doing does work:-
sqlite> .open 'C.db'
sqlite> ATTACH 'A.db' AS db1;
sqlite> ATTACH 'B.db' AS db2;
sqlite> PRAGMA database_list;
0|main|C:\Users\Mike\C.db
2|db1|C:\Users\Mike\A.db
3|db2|C:\Users\Mike\B.db
sqlite> CREATE TABLE IF NOT EXISTS main.C (ColumnInCommon);
sqlite> CREATE TABLE IF NOT EXISTS db1.A (ColumnInCommon);
sqlite> CREATE TABLE IF NOT EXISTS db2.B (ColumnInCommon);
sqlite> SELECT * FROM db1.A INNER JOIN db2.B ON B.ColumnInCommon = A.ColumnInCommon;
sqlite>
i.e. no error.
I can force an error using :-
sqlite> SELECT * FROM dbx.A INNER JOIN db2.B ON B.ColumnInCommon = A.ColumnInCommon;
Error: no such table: dbx.A
sqlite>
But this shouldn't apply as your ATTACH appears to have worked (pragma database_list; will confirm as used above). As such it is probably that the table itself does not exist. I'd suggest trying :-
SELECT * FROM db1.sqlite_master;
This would then show the tables in the A(db1) database e.g. :-
sqlite> SELECT * FROM db1.sqlite_master;
table|A|A|2|CREATE TABLE A (ColumnInCommon)
sqlite>

SQLite insert into table using the id from an inner insert

I'm trying to insert a parent and child at the same time.
My idea is to insert the parent, get the id using SELECT last_insert_rowid() AS [Id] and use this id to insert the child
I can get each part of this working independently but not as a whole. This is what I currently have:
INSERT INTO ParentTable (Col1)
VALUES( 'test')
SELECT last_insert_rowid() AS [Id]
The above works - so far so good. Now I want to use the result of this in the child insert. This is what I have:
INSERT INTO ChildTable (col1, col2, ParentId)
VALUES( 1, 2, SELECT Id FROM (
INSERT INTO ParentTable (Col1)
VALUES( 'test')
SELECT last_insert_rowid() AS [Id]
);
I get this error:
near "SELECT": syntax error:
Can anyone point me in the right direction?
You can't use INSERT in SELECT statement. You should first insert and then use last inserted id:
INSERT INTO ParentTable (Col1) VALUES( 'test');
INSERT INTO ChildTable (col1, col2, ParentId)
VALUES(1,2, (SELECT last_insert_rowid()));
Since you want to insert many records with parent ID, here is a workaround:
BEGIN TRANSACTION;
CREATE TEMPORARY TABLE IF NOT EXISTS temp(id integer);
DELETE FROM temp;
INSERT INTO ParentTable (Col1) VALUES( 'test');
INSERT INTO temp SELECT last_insert_rowid();
INSERT INTO ChildTable (col1, col2, ParentId)
VALUES(1,2, (SELECT id FROM temp LIMIT 1));
.............
COMMIT;
DROP TABLE temp;
Or you can create a permanent table to this effect.
That SQLite.Net PCL driver assumes that you use the ORM: inserting an object will automatically read back and assign the autoincremented ID value.
If you're using raw SQL, you have to manage the last_insert_rowid() calls yourself.
Your idea is correct, but you have to do everything in separate SQL statements:
BEGIN; -- better use RunInTransaction()
INSERT INTO Parent ...;
SELECT last_insert_rowid(); --> store in a variable in your program
INSERT INTO Child ...;
...
END;
(SQLite is an embedded database and has no client/server communication overhead; there is no reason to try to squeeze everything into a single statement.)

ssdt: change the types of primary key/secondary keys

Original table1 and Table2. Both tables has data.
CREATE TABLE [dbo].[Table1]
(
[Id] int NOT NULL PRIMARY KEY
)
CREATE TABLE [dbo].[Table2]
(
[Id] INT NOT NULL PRIMARY KEY,
[Table1Id] Int NULL,
Constraint [FK_Table1_Table2] foreign key ([Table1Id]) references [Table1] (Id)
)
I'd like to change the Table1.Id to UNIQUEIDENTIFIER.
Obviously just jump in and change the type from int to UNIQUEIDENTIFIER for Table1.Id and 'Table2.Table1Id'. Then Publish. Here is the code:
CREATE TABLE [dbo].[tmp_ms_xx_Table1] (
[Id] UNIQUEIDENTIFIER NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
IF EXISTS (SELECT TOP 1 1
FROM [dbo].[Table1])
BEGIN
INSERT INTO [dbo].[tmp_ms_xx_Table1] ([Id])
SELECT [Id]
FROM [dbo].[Table1]
ORDER BY [Id] ASC;
END
This code will fail because original Table1.Id is Int while temp table Id is UNIQUEIDENTIFIER.
Then, I try to with Pre-Scripts. Ideally all the changes will be done manually.
--drop fk constraint
alter table [Table2] drop constraint [FK_Table1_Table2];
--rename table1.id
exec sp_rename 'Table1.Id', 'Id2', 'COLUMN';
alter table [Table1] add Id uniqueidentifier not null
default newid();
--rename table2.table1id
exec sp_rename 'Table2.Table1Id', 'Table1Id2', 'COLUMN';
alter table [Table2] add Table1Id uniqueidentifier null;
update t2 set t2.Table1ID = t1.Id
from Table2 t2 left join Table1 t1 on t2.Table1Id2 = t1.Id2;
alter table [Table2] add constraint [FK_Table1_Table2] foreign key (Table1Id) references Table1 (Id);
However it FAIL again as SSDT is trying to compare its data structure again the target database.
Any idea please?
You're right. The problem is that you can't include schema changes in the pre-deployment script because SSDT's deployment script is generated prior to your schema changes. It is therefore only useful for data-only changes.
The solution is to do this outside of the SSDT process altogether. Yes, it's a pre-pre-deployment script! Essentially you have to apply your change by yourself before you even get to the SSDT bit.
(There's probably a way to do this via a custom deployment contributor. After all, everything is possible in code...)
Can I convince you to take a look at a migration-based solution as it appears that you have sufficient need for an element of fine-grained script "customisation". DBUp is a popular open source solution. ReadyRoll is a more-integrated commercial solution that shares a lot with SSDT.
the problem is the old data. it will be ok without the data in table in step 2.
1.pre-script: copy/process old data to temp tables, delete them from original tables
create table #table1 (
id int null,
id2 uniqueidentifier null
);
insert into #table1 (id,id2)
select id,newid() from Table1;
create table #table2 (
id int null,
table1id int null,
table1id2 uniqueidentifier null
);
insert into #table2 (id, table1id)
select id,table1id from Table2;
update t2 set t2.table1id2=t1.id2
from #table2 t2 left join #table1 t1 on t2.table1id = t1.id;
delete from table2;
delete from table1;
dacpac will auto generate the changes for schema. it will be ok because no data is existing any more.
post-script: insert data back from temp tables in pre-script:
insert into table1 (id)
select id2 from #table1;
insert into table2 (id,table1id)
select id, table1id2 from #table2;

Sqlite update query with table shotcuts

I am getting problem for UPDATE query in sqlite .
UPDATE Table1 T1, Table2 T2 SET T1.USE_MHE = T2.USE_MHE WHERE T1.EQ_NAME= T2.EQ_NAME
Above query works fine for MS access ADO connection.
But for SQLite it's looks like using table shortcut is not possible (Table1 T1) I have 100's of such query to update. Please let me know how table shortcut can be used in SQLite update query.,
Your problem is not aliases that you're using. Your UPDATE clause is wrong. Try:
UPDATE
Table1 AS T1
SET
T1.USE_MHE = (SELECT T2.USE_MHE FROM Table2 AS T2 WHERE T1.EQ_NAME = T2.EQ_NAME)
WHERE
EXISTS(SELECT * FROM Table2 AS T2 WHERE T1.EQ_NAME = T2.EQ_NAME);

How can I get the list of a columns in a table for a SQLite database?

I am looking to retrieve a list of columns in a table. The database is the latest release of SQLite (3.6, I believe). I am looking for code that does this with a SQL query. Extra bonus points for metadata related to the columns (e.g. length, data type, etc...)
What you're looking for is called the data dictionary. In sqlite a list of all tables can be found by querying sqlite_master table (or view?)
sqlite> create table people (first_name varchar, last_name varchar, email_address varchar);
sqlite> select * from sqlite_master;
table|people|people|2|CREATE TABLE people (first_name varchar, last_name varchar, email_address varchar)
To get column information you can use the pragma table_info(table_name) statement:
sqlite> pragma table_info(people);
0|first_name|varchar|0||0
1|last_name|varchar|0||0
2|email_address|varchar|0||0
For more information on the pragma statements, see the documentation.
Here's the simple way:
.schema <table>
The question is old but the following hasn't been mentioned yet.
Another convenient way in many cases is to turn headers on by:
sqlite> .headers on
Then,
sqlite> SELECT ... FROM table
will display a headline showing all selected fields (all if you SELECT *) at the top of the output.
Here's a SELECT statement that lists all tables and columns in the current database:
SELECT m.name as tableName,
p.name as columnName
FROM sqlite_master m
left outer join pragma_table_info((m.name)) p
on m.name <> p.name
order by tableName, columnName
;
just go into your sqlite shell:
$ sqlite3 path/to/db.sqlite3
and then just hit
sqlite> .schema
and you will get everything.
This is a query that lists all tables with their columns, and all the metadata I could get about each column as OP requested (as bonus points).
SELECT
m.name AS table_name,
p.cid AS col_id,
p.name AS col_name,
p.type AS col_type,
p.pk AS col_is_pk,
p.dflt_value AS col_default_val,
p.[notnull] AS col_is_not_null
FROM sqlite_master m
LEFT OUTER JOIN pragma_table_info((m.name)) p
ON m.name <> p.name
WHERE m.type = 'table'
ORDER BY table_name, col_id
Thanks to #David Garoutte for showing me how to get pragma_table_info to work in a query.
Run this query to see all the table metadata:
SELECT * FROM sqlite_master WHERE type = 'table'
Building on the above, you can do it all at once:
sqlite3 yourdb.db ".schema"
That will give you the SQL to create the table, which is effectively a list of the columns.
In case if you want to get all column names into one single comma separated string, you can use below.
SELECT GROUP_CONCAT(NAME,',') FROM PRAGMA_TABLE_INFO('table_name')
Here the pragma table_info is used as pragma_table_info for the select statement and GROUP_CONCAT is to combine all the field names into one string. for the second parameter of GROUP_CONCAT you can pass the separator.
I know, it’s been a long time but it’s never too late…
I had a similar question with TCL as interpreter and after several search, found nothing good for me. So I propose something based on PRAGMA, knowing that your DB is “main”
db eval { PRAGMA main.table_info(<your table name>) } TBL { puts $TBL(name) }
And array use to obtain a list
set col_list {}
db eval { PRAGMA main.table_info(<your table name>) } TBL { lappend col_list $TBL(name) }
puts $col_list

Resources