Deleting rows not existing in another table with composite primary key's - sqlite

I'm using SQLite and are trying to move old rows from a table Students to Students_Old, and copy new rows from Students_Import.
The problem is I have multiple primary keys as this:
CREATE TABLE "Students" (
`LastName` TEXT NOT NULL,
`FirstName` TEXT NOT NULL,
`BornDate` TEXT NOT NULL,
`Class` TEXT NOT NULL,
`Photo` TEXT,
`ValidUntil` CHAR(10),
PRIMARY KEY(LastName,FirstName,BornDate))
All tables have this structure (except Students_Import that's missing Photo and ValidUntil).
So far I have managed to copy the old rows with this:
INSERT INTO Students_Old
SELECT DISTINCT a.LastName, a.FirstName, a.BornDate, a.Class, a.Photo, a.ValidUntil FROM
Students a LEFT JOIN Students_Import b ON a.LastName =b.LastName AND a.FirstName=b.FirstName AND a.BornDate=b.BornDate WHERE b.LastName is NULL;
And add the new rows with this:
INSERT INTO Students
SELECT DISTINCT a.LastName, a.FirstName, a.BornDate, a.Class, "", "" FROM
Students_Import a LEFT JOIN Students b ON a.LastName =b.LastName AND a.FirstName=b.FirstName AND a.BornDate=b.BornDate WHERE b.LastName is NULL
But I can't figure out how to delete old rows in Students (that don't exist in Students_Import).
I have tried a few variants of this:
DELETE FROM Students WHERE (LastName, FirstName, BornDate) IN
(SELECT DISTINCT a.LastName, a.FirstName, a.BornDate, a.Class, a.Photo, a.ValidUntil FROM
Students a LEFT JOIN Students_Import b ON a.LastName =b.LastName AND a.FirstName=b.FirstName AND a.BornDate=b.BornDate WHERE b.LastName is NULL);
But I only get syntax error or that I can't use it on multiple rows.
I would appreciate any help!

IN does not work with multiple columns.
To find rows that do not exist in another table, use NOT EXISTS with a correlated subquery:
DELETE FROM Students
WHERE NOT EXISTS (SELECT 1
FROM Students_Import
WHERE Students_Import.LastName = Students.LastName
AND Students_Import.FirstName = Students.FirstName
AND Students_Import.BornDate = Students.BornDate);

I don't think you can have multiple columns in the IN clause.
How about:
DELETE Students FROM Students s LEFT JOIN Students_Import si ON (s.LastName = si.LastName AND s.FirstName = si.FirstName AND s.BornDate = si.BornDate) WHERE s.LastName IS NULL;

Related

Getting a min(date) AND max(date) AND their respective titles

I have three tables that I would like to select from
Table 1 has a bunch of static information about a user like their idnumber, name, registration date
Table 2 has the idnumber of the user, course number, and the date they registered for the course
Table 3 has the course number, and the title of the course
I am trying to use one query that will select the columns mentioned in table 1, with the most recent course they registered (name and date registered) as well as their first course registered (name and date registered)
Here is what I came up with
SELECT u.idst, u.userid, u.firstname, u.lastname, u.email, u.register_date,
MIN(l.date_inscr) as mindate, MAX(l.date_inscr) as maxdate, lc.coursename
FROM table1 u,table3 lc
LEFT JOIN table2 l
ON l.idCourse = lc.idCourse
WHERE u.idst = 12787
AND u.idst = l.idUser
And this gives me everything i need, and the dates are correct but I have no idea how to display BOTH of the names of courses. The most recent and the first.
And help would be great.
Thanks!!!
You can get your desired results by generating the min/max date_inscr for each user in a derived table and then joining that twice to table2 and table3, once to get each course name:
SELECT u.idst, u.userid, u.firstname, u.lastname, u.email, u.register_date,
l.mindate, lc1.coursename as first_course,
l.maxdate, lc2.coursename as latest_course
FROM table1 u
LEFT JOIN (SELECT idUser, MIN(date_inscr) AS mindate, MAX(date_inscr) AS maxdate
FROM table2
WHERE idUser = 12787
) l ON l.idUser = u.idst
LEFT JOIN table2 l1 ON l1.idUser = l.idUser AND l1.date_inscr = l.mindate
LEFT JOIN table3 lc1 ON lc1.idCourse = l1.idCourse
LEFT JOIN table2 l2 ON l2.idUser = l.idUser AND l2.date_inscr = l.maxdate
LEFT JOIN table3 lc2 ON lc2.idCourse = l2.idCourse
As #BillKarwin pointed out, this is more easily done using two separate queries.

SQLITE, Create a temp table then select from it

just wondering how i can create a temp table and then select from it further down the script.
Example.
CREATE TEMPORARY TABLE TEMP_TABLE1 AS
Select
L.ID,
SUM(L.cost)/2 as Costs,
from Table1 L
JOIN Table2 C on L.ID = C.ID
Where C.name = 'mike'
Group by L.ID
Select
Count(L.ID)
from Table1 L
JOIN TEMP_TABLE1 TT1 on L.ID = TT1.ID;
Where L.ID not in (TT1)
And Sum(L.Cost) > TT1.Costs
Ideally I want to have a temp table then use it later in the script to reference from.
Any help would be great!
You simply refer to the table as temp.<table> or <table> the latter only if it is a unique table name.
As per :-
If a schema-name is specified, it must be either "main", "temp", or
the name of an attached database. In this case the new table is
created in the named database. If the "TEMP" or "TEMPORARY" keyword
occurs between the "CREATE" and "TABLE" then the new table is created
in the temp database. It is an error to specify both a schema-name and
the TEMP or TEMPORARY keyword, unless the schema-name is "temp". If no
schema name is specified and the TEMP keyword is not present then the
table is created in the main database.
SQL As Understood By SQLite - CREATE TABLE
The following example creates 3 tables :-
table1 with 3 columns as a permanent table.
table1 a temporary copy of the permanent table1.
temp_table another temporary copy of the permanent table1.
:-
DROP TABLE IF EXISTS temp.table1;
DROP TABLE IF EXISTS table1;
DROP TABLE IF EXISTS temp_table;
CREATE TABLE table1 (columnA INTEGER,columnB INTEGER, columnC INTEGER);
When creating the permanent table 1 it is loaded with 4 rows
:-
INSERT INTO table1 (columnA,columnB,columnC) VALUES
(1,5,20),
(2,7,21),
(3,8,80),
(4,3,63);
CREATE TEMP TABLE table1 AS select * from table1;;
CREATE TEMPORARY TABLE temp_table AS SELECT * FROM table1;
both temp tables are then used to in a union all to basically duplicate the rows, but with an indicator of the source table as a new column from_table
Not that two forms of referring to the temp tables are used. temp. and just the table name.
The latter only usable if the temporary table is a unique table name.
:-
SELECT 'temp_table' AS from_table,* FROM temp_table
UNION ALL
SELECT 'temp.table1' as from_table,* FROM temp.table1;
The result being :-
Re addition of example :-
CREATE TEMPORARY TABLE TEMP_TABLE1 AS
Select
L.ID,
SUM(L.cost)/2 as Costs,
from Table1 L
JOIN Table2 C on L.ID = C.ID
Where C.name = 'mike'
Group by L.ID
Select
Count(L.ID)
from Table1 L
JOIN TEMP_TABLE1 TT1 on L.ID = TT1.ID;
Where L.ID not in (TT1)
And Sum(L.Cost) > TT1.Costs
There are a few issues with this example bar the misuse of the aggregate (commented out) the following works.
Note for my convenience I've added an _ to the table names.
:-
DROP TABLE IF EXISTS Table_1;
DROP TABLE IF EXISTS Table_2;
DROP TABLE If EXISTS temp.temp_table1;
CREATE TABLE Table_1 (ID INTEGER PRIMARY KEY, cost REAL);
CREATE TABLE Table_2 (ID INTEGER PRIMARY KEY, name TEXT);
INSERT INTO Table_1 (cost) VALUES (100.45),(56.78),(99.99);
INSERT INTO Table_2 (name) VALUES ('mike'),('mike'),('fred');
CREATE TEMP TABLE temp_table1 AS
SELECT L.ID,
sum(L.cost)/2 as Costs
FROM Table_1 L
JOIN Table_2 C ON L.ID = C.ID
WHERE C.name = 'mike'
GROUP BY L.ID;
SELECT
count(L.ID)
FROM Table_1 L
JOIN temp_table1 TT1 ON L.ID = TT1.[L.ID]
WHERE
L.ID NOT IN (TT1.[L.ID])
-- AND Sum(L.cost) > TT1.costs --<<<< misuse of aggregate
The issues are based upon the column name being L.ID so this has to be enclosed (rules here SQL As Understood By SQLite - SQLite Keywords apply) [ and ] have been used above.
of course you could circumvent the need for enclosure by naming the column using AS e..g SELECT
L.ID AS lid, --<<<< AS lid ADDED
SUM(L.cost)/2 as Costs, ,.......
Adding the following may be suitable for getting around the misuse of aggregate :-
GROUP BY L.ID
HAVING sum(L.cost) > TT1.costs
Adding the following to the end of the script :-
SELECT
count(L.ID), *
FROM Table_1 L
JOIN temp_table1 TT1 ON L.ID = TT1.[L.ID];
results in :-
If this is only to be used by one SELECT statement then you can use the WITH clause:
WITH TmpTable(id,cost) AS
(
...SELECT statement that returns the two columns (id and cost)...
)
SELECT id, cost FROM TmpTable WHERE ...;

Get names for IDs in two columns in one from another table

I have two tables:
CREATE TABLE tElements (
elementID INTEGER,
name TEXT,
area TEXT,
zone TEXT,
voltageLevel TEXT,
mRID TEXT
);
CREATE TABLE tCAResults (
timestamp INTEGER NOT NULL,
outageElementID INTEGER NOT NULL,
monitoredElementID INTEGER NOT NULL,
preOutageLoading DOUBLE NOT NULL,
postOutageLoading DOUBLE NOT NULL
);
How can I create query where id's of outageElementID and monitoredElementID from table tCAResult would be displayed as names from table tElements?
I have been searching for a whole day but couldn't find the answer. The closest I found is this but can't work it out
A simple join or two will do the job:
select tc.timestamp, oe.name as outageElement, me.name as monitoredElement
from tCAResults tc
join tElements oe on (oe.elementID = tc.outageELementID)
join tElements me on (me.elementID = tc.monitoredElementID);

List un-used ID

I have a DropDownList that is populated with the primary key of Table A. Table B uses Table A primary key as a foreign key. The foreign key can only be used once.
So currently my list always populates with: 1, 2, 3, 4, 5 (exists as primary in Table A). Table B is already using keys: 1, 2, 3. When they keys are already existing in Table B, I need them not to populate in the dropdownlist.
I have tried to state in an SQL query, to only populate keys that don't exist in Table B. The query I'm trying, but doesn't work is:
SELECT Table_A.Grades_ID FROM Table_A INNER JOIN Table_B ON Table_A_ID = Table_B.Grades_ID WHERE Table_A.Grades_ID != Table_B.Grades_ID
How do I get my dropdownlist to not populate keys (data) that already exists in Table B?
Just adjust your SQL to exclude the PKey values that already exist in TableB
SELECT Table_A.Grades_ID
FROM
Grades
LEFT JOIN Table_B ON Table_A_ID = Table_B.Grades_ID
WHERE Table_B.Grades_ID Is Null
Alternatively, you may wish to use "not in"
SELECT
Tables_A.Grades_ID
WHERE
Tables_A.Grades_ID not in (Select Grades_ID from Table_B)
Try this:
select table_a.grades_id
from table_a left outer join
on table_a.grades_id = table_b.grades_id
where table_b.<some other field> is null
SELECT Grades_ID FROM Grades WHERE Grades_ID NOT IN (SELECT Grades_ID FROM Table_B)
Select Table_A.Grades_ID
from Table_A
where Table_A.Grades_ID NOT IN (Select Grades_ID from Table_B)

How to add another column to SELECT DISTINCT query for other columns?

I have this table
CREATE TABLE APmeasure
(id_APmeasure INTEGER PRIMARY KEY AUTOINCREMENT
, RSSI TEXT, TimeOfMeasure DATETIME
, BSSID TEXT, id_APm INTEGER NOT NULL
, FOREIGN KEY (id_APm) REFERENCES APTable (id_Ap) ON DELETE CASCADE)
I want to make a query which would give me distinct results of TimeOfMeasure and BSSID, like this:
SELECT DISTINCT TimeOfMeasure, BSSID
FROM APmeasure
WHERE "condition"
But that would retrieve me the other columns on the table, related to the DISTINCT query.
How do I do it?
Perform distinct/grouping operation,
Join to result of distinct/grouping operation...
Something like:
SELECT [whichever columns you want]
FROM APmeasure
JOIN (
SELECT TimeOfMeasure, BSSID
FROM APmeasure
WHERE [condition]
GROUP BY TimeOfMeasure, BSSID
) x
ON x.TimeOfMeasure = APmeasure.TimeOfMeasure
AND x.BSSID = APmeasure.BSSID
[any other joins you need]

Resources