How to write SQLite update operation in two tables? - sql

I am using SQLite,
TABLE A
(
ID
FileNAME
FOLDERID
)
TABLE B
(
FOLDERID
FOLDERPATH
)
I want to write a SQL statement to delete all files in A where its Folder is subfolder of C:\ABC\;
How can I make it in one SQLite statement, and is it the best way to do?
Many thanks!

the following works fine:
create table table_a (
id int,
file_name varchar(300),
folder_id int
);
create table table_b (
folder_id int,
folder_path varchar(300)
);
insert into table_a (id, file_name, folder_id) values (1, 'file1.txt', 1);
insert into table_a (id, file_name, folder_id) values (1, 'file2.txt', 1);
insert into table_a (id, file_name, folder_id) values (1, 'file2-1.txt', 2);
insert into table_b (folder_id, folder_path) values (1, 'c:\abc\somefolder\another');
insert into table_b (folder_id, folder_path) values (2, 'c:\abcNOT\somefolder\another');
delete
from table_a
where folder_id in (select folder_id from table_b where substr(folder_path, 1, 7) = 'c:\abc\');
select * from table_a;

Same idea but with some modifications:
SQLite recognize referential integrity now:
CREATE TABLE Table_a (
id int,
file_name varchar(300),
folder_id int,
FOREIGN KEY(folder_id) REFERENCES Table_b (folder_id)
);
CREATE TABLE Table_b (
folder_id int,
folder_path varchar(300)
);
CREATE TABLE Table_b (folder_id, folder_path) VALUES (1, 'c:\abc\somefolder\another');
CREATE TABLE Table_b (folder_id, folder_path) VALUES (2, 'c:\abcNOT\somefolder\another');
CREATE TABLE Table_a (id, file_name, folder_id) VALUES (1, 'file1.txt', 1);
CREATE TABLE Table_a (id, file_name, folder_id) VALUES (1, 'file2.txt', 1);
CREATE TABLE Table_a (id, file_name, folder_id) VALUES (1, 'file2-1.txt', 2);
DELETE
FROM Table_a
WHERE folder_id IN (SELECT folder_id FROM Table_b WHERE folder_path LIKE 'c:\abc\%');
SELECT * FROM Table_a;

Related

How can I run the SQL like "Insert into table_A where columnA = temp_table_result"?

I have two temp_tables defined as:
with temp_table_1 as (
select id from table_A where fname="john" limit 1
),
with temp_table_2 as (
select id from table_B where lname="smith" limit 1
)
These tables returns 1 row with just the id. I want to Insert a row into table_C where the column col_1 is equal to the result of temp_table_1 and the column col_2 is equal to the result of temp_table_2, and the third column (created_at) set to now:
The following fails for me:
insert into table_c (col_1, col_2, created_at)
values (temp_table_1.id, temp_table_2.id, current_timestamp at time zone 'UTC');
BEGIN;
CREATE temp TABLE table_a (
id bigint,
fname text
);
CREATE temp TABLE table_b (
id bigint,
lname text
);
CREATE temp TABLE table_c (
col_1 bigint,
col_2 bigint,
created_at timestamp
);
INSERT INTO table_a
VALUES (1, 'john');
INSERT INTO table_a
VALUES (1, 'john');
INSERT INTO table_b
VALUES (2, 'smith');
INSERT INTO table_b
VALUES (2, 'smith');
COMMIT;
WITH temp_table_1 AS (
SELECT
a.id AS a_id,
b.id AS b_id
FROM
table_a a,
table_b b
WHERE
a.fname = 'john'
AND b.lname = 'smith'
LIMIT 1)
INSERT INTO table_c (col_1, col_2, created_at)
SELECT
temp_table_1.a_id,
temp_table_1.b_id,
CURRENT_TIMESTAMP at time zone 'UTC'
FROM
temp_table_1
RETURNING
*;
That should be fairly straightforward with a cross join:
WITH temp_table_1 AS (... LIMIT 1),
WITH temp_table_2 AS (... LIMIT 1)
INSERT INTO table_c (col_1, col_2, created_at)
SELECT temp_table_1.id,
temp_table_2.id,
current_timestamp AT TIME ZONE 'UTC'
FROM temp_table_1 CROSS JOIN temp_table_2;

Update multiple tables in trigger where one of the tables is used for trigger activation

Let's say I have two tables called widgetCustomer and widgetSale. On an insert in widgetSale I want to add a timestamp to the widgetSale row and add the sale id as last_order_id to the widgetCustomer table.
I understand using AFTER INSERT ON will result in an error on trying to update the NEW row, hence we need to use BEFORE INSERT ON clause. Which brings forward a new issue that AUTO_INCREMENT has not yet generated a id for sale hence last_order_id would all be zero. There is a method to do this at MySQL/MariaDB TRIGGER but it seems to fail on my system (i.e., the last order ids are still zero).
As a work around I'm using two different triggers one before insert and one after insert. Although it does work I'm keen to learn if there is a possible flaws with the method above and is there a better way of doing this (both in terms of performance and data integrity).
My code is given below:
DROP TABLE IF EXISTS widgetSale;
DROP TABLE IF EXISTS widgetCustomer;
DROP TABLE IF EXISTS widgetLog;
CREATE TABLE widgetCustomer ( id integer primary key AUTO_INCREMENT, name TEXT, last_order_id INT, stamp TEXT );
CREATE TABLE widgetSale ( id integer primary key AUTO_INCREMENT, item_id INT, customer_id INTEGER, quan INT, price INT, stamp TEXT );
CREATE TABLE widgetLog ( id integer primary key AUTO_INCREMENT, stamp TEXT, event TEXT, username TEXT, tablename TEXT, table_id INT);
INSERT INTO widgetCustomer (name) VALUES ('Bob');
INSERT INTO widgetCustomer (name) VALUES ('Sally');
INSERT INTO widgetCustomer (name) VALUES ('Fred');
SELECT * FROM widgetCustomer;
CREATE TRIGGER stampSale BEFORE INSERT ON widgetSale
FOR EACH ROW BEGIN
SET NEW.stamp = CURRENT_TIMESTAMP();
END
CREATE TRIGGER stampOnRest AFTER INSERT ON widgetSale
FOR EACH ROW BEGIN
UPDATE widgetCustomer SET last_order_id = NEW.id, stamp = CURRENT_TIMESTAMP()
WHERE widgetCustomer.id = NEW.customer_id;
INSERT INTO widgetLog (stamp, event, username, tablename, table_id)
VALUES (CURRENT_TIMESTAMP(), 'INSERT', 'TRIGGER', 'widgetSale', NEW.id);
END
INSERT INTO widgetSale (item_id, customer_id, quan, price) VALUES (1, 3, 5, 1995);
INSERT INTO widgetSale (item_id, customer_id, quan, price) VALUES (2, 2, 3, 1495);
INSERT INTO widgetSale (item_id, customer_id, quan, price) VALUES (3, 1, 1, 2995);
SELECT * FROM widgetSale;
SELECT * FROM widgetCustomer;
SELECT * FROM widgetLog;
I'm using mariadb 10.6.* on Archlinux.

How to query a table with 2 foreign keys?

I have three tables in my android app.
Table Animal Table Group TableGroup_Animal
idAnimal idGroup idAnimal
AnimalName groupName idGroup
I want to know how to do query in order to get all the animals inside a group. The query I made is this
select
Groups.groupName
from
Groups,
Animal,
tableGrupo_Animal
where
Animal.idanimal = tableGroup_Animal.fkanimal
and
Groups.idgrupo = tableGrupo_Animal.fkgrupo
group by
group.groupName
And also this(That only shows the first row)
select
*
from
Animal
INNER JOIN " + tableGroup + " ON Animal.idanimal = Group.idGroup
where
Group.groupName = ' " + groupName +
As I said I want to get all the animals inside a group, how can I do that? Thanks
I recreated your database in sqlite and tested this out. This SQL works fine. I'd suggest that you rename your table "Group" as it's a reserved word in SQL and you'll have to back tick it every time as I do in this sql.
select a.AnimalName, g.groupName
from Animal a
JOIN TableGroup_Animal tga ON a.idAnimal = tga.idAnimal
JOIN `Group` g on g.idGroup= tga.idGroup;
Here's the schema I created and data to help anyone trying to prototype this.
CREATE TABLE `Animal` (
`idAnimal` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
`animalName` varchar(128)
);
CREATE TABLE `Group` (
`idGroup` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
`groupName` varchar(128)
);
CREATE TABLE `TableGroup_Animal` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
`idAnimal` integer,
`idGroup` integer
);
--Seeding animal
insert into `Animal` (animalName) values ('cat');
insert into `Animal` (animalName) values ('Rat');
insert into `Animal` (animalName) values ('Zebra');
insert into `Animal` (animalName) values ('Platypus');
--seeding group
insert into `Group` (groupName) values ('small');
insert into `Group` (groupName) values ('medium');
insert into `Group` (groupName) values ('large');
---seeding group animal pivot table
insert into TableGroup_Animal (idAnimal, idGroup) values (3,2);
insert into TableGroup_Animal (idAnimal, idGroup) values (3,3);
insert into TableGroup_Animal (idAnimal, idGroup) values (1,2);
insert into TableGroup_Animal (idAnimal, idGroup) values (4,2);
insert into TableGroup_Animal (idAnimal, idGroup) values (2,1);
);
insert into TableGroup_Animal (idAnimal, idGroup) values (2,1);
Do this query:
select * from Animal INNER JOIN tableGrupo_Animal ON Animal.idanimal = tableGrupo_Animal.fkanimal INNER JOIN Grupos ON Grupos.idgrupo = tableGrupo_Animal.fkgrupo where Grupos.nomegrupo='wwrt'

Inserting the Identity of another Insert from Select?

Is This Possible?
Here is something I'm looking for, executed together:
First, it would execute the INSERT based on how many rows in the SELECT
INSERT INTO TABLE2 (xID, NAME)
SELECT xID, NAME FROM TABLE
Then getting the ##IDENTITY of each INSERTED ROW, it would create a new Insert including the same data of the first SELECT statement:
INSERT INTO TABLE3 (xID, NAME, ID)
SELECT xID, NAME, ID as Scope_IdentitY()
If not, what the best way without using cursor or while?
You have, at least, two options:
1) The OUTPUT...INTO target_table clause (SQL2005+)
2) Or you could write composable DML(SQL2008+).
Example:
DECLARE #Table2 TABLE(
ID INT IDENTITY PRIMARY KEY, --IDENTITY
xID INT NOT NULL,
NAME VARCHAR(25) NOT NULL
);
DECLARE #Table3 TABLE(
ID INT PRIMARY KEY, --No IDENTITY
xID INT NOT NULL,
NAME VARCHAR(25) NOT NULL
);
--First solution: OUTPUT ... INTO
INSERT INTO #Table2 (xID, NAME)
OUTPUT inserted.xID, inserted.NAME, inserted.ID INTO #Table3(xID, NAME, ID)
SELECT t.Col1, t.Col2
FROM (SELECT 11,'A' UNION ALL SELECT 22,'B' UNION ALL SELECT 33,'C') AS t(Col1,Col2);
--Second solution: composable DML
INSERT INTO #Table3(xID, NAME, ID)
SELECT src.xID, src.NAME, src.ID
FROM
(
INSERT INTO #Table2 (xID, NAME)
OUTPUT inserted.xID, inserted.NAME, inserted.ID
SELECT t.Col1, t.Col2
FROM (VALUES(44,'D'),(55,'E'),(66,'F')) AS t(Col1,Col2)
) src
SELECT * FROM #Table2
SELECT * FROM #Table3
INSERT INTO TABLE2 (xID, NAME)
OUTPUT
INSERTED.xID, INSERTED.NAME, INSERTED.ID
INTO TABLE3 (xID, NAME, ID)
SELECT xID, NAME FROM [TABLE]
You can declare a table variable and store the output of the rows inserted into dbo.Table2 in this variable and use the table variable as the input for table dbo.Table3.
CREATE TABLE dbo.Table1
(
xid int NOT NULL
, name varchar(30) NOT NULL
);
CREATE TABLE dbo.Table2
(
id int NOT NULL IDENTITY
, xid int NOT NULL
, name varchar(30) NOT NULL
);
CREATE TABLE dbo.Table3
(
id int NOT NULL
, xid int NOT NULL
, name varchar(30) NOT NULL
);
INSERT INTO dbo.Table1 (xid, name) VALUES
(195, 'abc'),
(242, 'def'),
(332, 'ghi');
GO
DECLARE #tempTable table
( id int
, xid int
, name varchar(30)
);
INSERT dbo.Table2
OUTPUT INSERTED.id, INSERTED.xid, INSERTED.name
INTO #tempTable
SELECT xid, name FROM dbo.Table1;
INSERT dbo.Table3 (id, xid, name)
SELECT id, xid, name FROM #tempTable;
SELECT id, xid, name FROM dbo.Table2;
SELECT id, xid, name FROM dbo.Table3;
GO
OK, based on your comments below, try this:
INSERT INTO TABLE2 (xID, NAME)
SELECT xID, NAME FROM TABLE;
INSERT INTO TABLE3 (xID, NAME, ID)
SELECT xID, NAME, ##identity
FROM TABLE2;
Assuming these table structures:
TABLE_A
-----------
X_ID
NAME
TABLE_B
----------------
TABLE_B_ID [PK]
X_ID
NAME
TABLE_C
----------------
TABLE_C_ID [PK]
X_ID
NAME
TABLE_B_ID [FK]
Then wouldn't this work (best in a transaction)?:
-- Grab data from TABLE_A and INSERT INTO TABLE_B
INSERT INTO TABLE_B (
X_ID,
NAME
)
SELECT
X_ID,
NAME
FROM
TABLE_A
-- Grab data from TABLE_B that matches the data imported from TABLE_A
-- and INSERT that data into TABLE_C (incl. the PK from TABLE_B)
INSERT INTO TABLE_C (
X_ID,
NAME,
TABLE_B_ID
)
SELECT
b.X_ID,
b.NAME,
b.TABLE_B_ID
FROM
TABLE_B b
INNER JOIN
TABLE_A a ON a.X_ID = b.X_ID

Querying a Many to Many Structure for a subset of joined rows

I have a simple many to many db structure:
Table 1: ITEM
Columns:
ITEM_ID, ITEM_NAME
Table 2: Attribute
Columns:
ATTRIBUTE_ID, ATTRIBUTE_NAME
Table 3: ITEM_ATTRIBUTE
ITEM_ID, ATTRIBUTE_ID
What I want is to "get all items that have the following x attributes". X can be any number of attributes.
The best I've come up with is the following, but I believe there has to be a better way using joins and/or "select where in" clauses...but I can't think of it.
SELECT * FROM Item
WHERE Item.ITEM_ID IN
(SELECT ITEM_ATTRIBUTE.item_ID FROM ITEM_ATTRIBUTE WHERE ITEM_ATTRIBUTE.attribute_ID =1)
and Item.ITEM_ID in
(SELECT ITEM_ATTRIBUTE.item_ID FROM ITEM_ATTRIBUTE WHERE ITEM_ATTRIBUTE.attribute_ID =3);
I'd rather not have to add an additional "ITEM_ID in (...) for each attribute in the list.. esp if the list of attributes is 20+ long
I would suggest populating a temporary table with the attributes that you would like to require. Once you have this table, the query becomes much cleaner and maintainable.
DECLARE #Item TABLE (
Item_Id INT,
Item_Name VARCHAR(50)
)
DECLARE #Attribute TABLE (
Attribute_Id INT,
Attribute_Name VARCHAR(50)
)
DECLARE #Item_Attribute TABLE (
Item_Id INT,
Attribute_Id INT
)
INSERT INTO #Item VALUES (1, 'Widget')
INSERT INTO #Item VALUES (2, 'Woozle')
INSERT INTO #Attribute VALUES (1, 'foo')
INSERT INTO #Attribute VALUES (2, 'bar')
INSERT INTO #Attribute VALUES (3, 'baz')
INSERT INTO #Attribute VALUES (4, 'qux')
INSERT INTO #Item_Attribute VALUES (1, 1)
INSERT INTO #Item_Attribute VALUES (1, 2)
INSERT INTO #Item_Attribute VALUES (1, 3)
INSERT INTO #Item_Attribute VALUES (2, 1)
INSERT INTO #Item_Attribute VALUES (2, 4)
DECLARE #Required_Attribute TABLE (
Attribute_Id INT
)
INSERT INTO #Required_Attribute VALUES (1)
INSERT INTO #Required_Attribute VALUES (2)
SELECT *
FROM #Item i
WHERE NOT EXISTS (
SELECT 1
FROM
#Required_Attribute ra
LEFT JOIN #Item_Attribute missingAttribute
ON ra.Attribute_Id = missingAttribute.Attribute_Id
AND missingAttribute.Item_Id = i.Item_Id
WHERE
missingAttribute.Attribute_Id IS NULL
)