SQL DELETE From with sub query syntax error - sql

I am having difficulty runing SQL code in MS Access 2010. I would like to ask for help in reviewing and correcting it.
Data:
- two tables with names: Tbl_001_WholeBase and Tbl_002_NewKVG
- they have connected by column named Key
Problem:
I want to delete all rows from Tbl_001_WholeBase which have Key that is not available in table Tbl_002_NewKVG
Example:
Tbl_001_WholeBase
ID Key
1 Hronic1
2 Hronic2
3 Hronic3
Tbl_002_NewKVG
ID Key
1 Hronic1
2 Hronic2
So as a result I would like to leave in Tbl_001_WholeBase only 3rd record, base would look like this:
ID Key
3 Hronic3
What I wanted to use in Access was:
DELETE
FROM Tbl_001_WholeBase
WHERE Tbl_001_WholeBase.KEY IN
(SELECT *
FROM Tbl_001_WholeBase
LEFT JOIN Tbl_002_NewKVG
ON Tbl_001_WholeBase.Key = Tbl_002_NewKVG.Key
WHERE (((Tbl_002_NewKVG.Key) Is Null)));
The subquery is working properly, however I can't connect it with Delete statement.
Error I got when running this code is:
You have written a subquery that can return more than one field without using the Exists reserved word in the main query's FROM clause. Revise the SELECT statement of the subquery to request only one field.

Select a key in subquery instead of (*)
DELETE
FROM Tbl_001_WholeBase
WHERE Tbl_001_WholeBase.KEY IN
(SELECT keyId
FROM Tbl_001_WholeBase
LEFT JOIN Tbl_002_NewKVG
ON Tbl_001_WholeBase.Key = Tbl_002_NewKVG.Key
WHERE (((Tbl_002_NewKVG.Key) Is Null)));
Here keyId will be your column name or your unique key through which you want to delete the row.

Join the two tables using a LEFT JOIN.
This will return all records from the table on the left of the join, and any matching records from the table on the right. NULL is used where the record on the right isn't available.
SELECT *
FROM Tbl_001_WholeBase LEFT JOIN Tbl_001_NewKVG ON Tbl_001_WholeBase.Key = Tbl_001_NewKVG.Key
| Tbl_001_WholeBase.ID | Tbl_001_WholeBase.Key | Tbl_001_NewKVG.ID | Tbl_001_NewKVG.Key |
|----------------------|-----------------------|-------------------|--------------------|
| 1 | Hronic1 | 1 | Hronic1 |
| 2 | Hronic2 | 2 | Hronic2 |
| 3 | Hronic3 | NULL | NULL |
You can see that the last NewKVG.Key is NULL, so you can omit it from the results:
SELECT *
FROM Tbl_001_WholeBase LEFT JOIN Tbl_001_NewKVG ON Tbl_001_WholeBase.Key = Tbl_001_NewKVG.Key
WHERE NOT Tbl_001_NewKVG.Key IS NULL
| Tbl_001_WholeBase.ID | Tbl_001_WholeBase.Key | Tbl_001_NewKVG.ID | Tbl_001_NewKVG.Key |
|----------------------|-----------------------|-------------------|--------------------|
| 1 | Hronic1 | 1 | Hronic1 |
| 2 | Hronic2 | 2 | Hronic2 |
Or you can delete it from the table:
DELETE DISTINCTROW Tbl_001_WholeBase.*
FROM Tbl_001_WholeBase LEFT JOIN Tbl_001_NewKVG ON Tbl_001_WholeBase.Key = Tbl_001_NewKVG.Key
WHERE NOT Tbl_001_NewKVG.Key IS NULL
| ID | Key |
|----------|----------|
| #Deleted | #Deleted |
| #Deleted | #Deleted |
| 3 | Hronic3 |

Use EXISTS:
DELETE FROM Tbl_001_WholeBase
WHERE EXISTS (SELECT 1
FROM Tbl_002_NewKVG
WHERE Tbl_001_WholeBase.Key = Tbl_002_NewKVG.Key
);
Or use IN with no JOIN:
DELETE FROM Tbl_001_WholeBase
WHERE Tbl_001_WholeBase.Key IN (SELECT Tbl_002_NewKVG.Key
FROM Tbl_002_NewKVG
);

Related

How to join multiple columns in one table to another lookup table?

I am unable to figure out how to join a couple of tables together when multiple columns in one table refer to another table.
For example, I have a "document_statuses" table:
document_statuses table:
+-----------+-------------+
| status_id | status_name |
+-----------+-------------+
| 1 | RECEIVED |
| 2 | MISSING |
| 3 | NOT_NEEDED |
+-----------+-------------+
Now in another table, I am tracking the status of multiple documents:
filings table:
+-----------+-------------+----------------+----------------+----------------+
| filing_id | filing_name | doc1_status_id | doc2_status_id | doc3_status_id |
+-----------+-------------+----------------+----------------+----------------+
| 1 | John | 1 | 3 | 2 |
| 2 | Mikaela | 2 | 3 | 2 |
| 3 | Sam | 1 | 2 | 1 |
+-----------+-------------+----------------+----------------+----------------+
How would I write a query that pulls the status_name in for each column and produce the following result:
+-------------+-------------+-------------+------------+
| Filing Name | Doc1 Status | Doc2 Status | Doc3Status |
+-------------+-------------+-------------+------------+
| John | RECEIVED | NOT_NEEDED | MISSING |
| Mikaela | MISSING | NOT_NEEDED | MISSING |
| Sam | RECEIVED | MISSING | RECEIVED |
+-------------+-------------+-------------+------------+
I'm aware of how to do this when looking up a single field from document_statuses per row, but not multiple. If I only had one column in documents that referred to document_statuses, I'd do a simple JOIN:
SELECT filing_name, status_name
FROM documents d
LEFT JOIN document_statuses ds ON d.doc1_status = ds.status_id
But how do I do that when I need more than one?
You will need to join the statuses table multiple times and then alias the columns in the select clause to be formatted to be what you want. Please note that you have to alias the tables in the join clause so that you can reference the columns in the select clause of the statement.
SELECT filing_name
, ds1.status_name AS 'Doc1 Status'
, ds2.status_name AS 'Doc2 Status'
, ds3.status_name AS 'Doc3 Status'
FROM documents d
LEFT JOIN document_statuses ds1 ON d.doc1_status = ds1.status_id
LEFT JOIN document_statuses ds2 ON d.doc2_status = ds2.status_id
LEFT JOIN document_statuses ds3 ON d.doc3_status = ds3.status_id
You'll need to do three times joins with same table using different aliases for table.
SELECT filing_name, ds.status_name, ds1.status_name,
ds2.status_name FROM documents d
LEFT JOIN document_statuses ds ON d.doc1_status
LEFT JOIN document_statuses ds1 ON d.doc1_status
LEFT JOIN document_statuses ds2 ON d.doc1_status
You can do this:
select t1.filing_name, t2.status_name as doc1_status, t3.status_name as doc2_status, t4.status_name as doc3_status
from filings_table t1
inner join statuses_table t2 on t1.doc1_status_id = t2.status_id
inner join statuses_table t3 on t1.doc2_status_id = t3.status_id
inner join statuses_table t4 on t1.doc3_status_id = t4.status_id

Use JOIN on multiple columns multiple times

I am trying to figure out the best way to use a JOIN in MSSQL in order to do the following:
I have two tables. One table contains technician IDs and an example of one data set would be as follows:
+--------+---------+---------+---------+---------+
| tagid | techBid | techPid | techFid | techMid |
+--------+---------+---------+---------+---------+
| 1-1001 | 12 | 0 | 11 | 6 |
+--------+---------+---------+---------+---------+
I have another table that stores the names of these technicians:
+------+-----------+
| TTID | SHORTNAME |
+------+-----------+
| 11 | Steven |
| 12 | Mark |
| 6 | Pierce |
+------+-----------+
If the ID of a technician in the first table is 0, there is no technician of that type for that row (types are either B, P, F, or M).
I am trying to come up with a query that will give me a result that contains all of the data from table 1 along with the shortnames from table 2 IF there is a matching ID, so the result would look something like the following:
+--------+---------+---------+---------+---------+----------------+----------------+----------------+----------------+
| tagid | techBid | techPid | techFid | techMid | techBShortName | techPShortName | techFShortName | techMShortName |
+--------+---------+---------+---------+---------+----------------+----------------+----------------+----------------+
| 1-1001 | 12 | 0 | 11 | 6 | Mark | NULL | Steven | Pierce |
+--------+---------+---------+---------+---------+----------------+----------------+----------------+----------------+
I am trying to use a JOIN to do this, but I cannot figure out how to join on multiple columns multiple times to where it would look something like
Select table1.tagid, table1.techBid, table1.techPid, table1.techFid, table1.techMid, table2.shortname
FROM table1
INNER JOIN table2 on //Dont know what to put here
You need to use left joins like this:
Select table1.tagid, table1.techBid, table1.techPid, table1.techFid, table1.techMid,
t2b.shortname, t2p.shortname, t2f.shortname, t2m.shortname,
FROM table1
LEFT JOIN table2 t2b on table1.techBid = t2b.ttid
LEFT JOIN table2 t2p on table1.techPid = t2p.ttid
LEFT JOIN table2 t2f on table1.techFid = t2f.ttid
LEFT JOIN table2 t2m on table1.techMid = t2m.ttid
you just do mutiple left join
select tech.techPid, techPname.SHORTNAME
, tech.techFid, techFname.SHORTNAME
from tech
left join techName as techPname
on tech.techPid = techPname.TTID
left join techName as techFname
on tech.techFid = techFname.TTID

SQL Select Text for multiple foreign keys to lookup table in same row

I have a table similar to the following that has a history of changes to an item and holds the old and new value for a status. The status number is a foreign key to a lookup table that holds the text. I.e. 1 = 'In Inventory', 2= 'Destroyed' etc..
I want to be able to present this as human readable results and replace the integer keys with the text from the lookup table but I'm not quite sure how to do that as I can't just join on the foreign key.
Demo Database
+---------+-------------+-------------+------------+
| ITEM_ID | OLD_STATUS | NEW_STATUS | TIMESTAMP |
+---------+-------------+-------------+------------+
| 1 | 1 | 2 | 2012-03-25 |
| 1 | 2 | 3 | 2013-12-25 |
| 1 | 3 | 4 | 2015-03-25 |
+---------+-------------+-------------+------------+
You can join on the status table multiple times - something like this:
select i.item_id,
i.old_status,
i.new_status,
i.timestamp,
s1.statustext,
s2.statustext
from items i
join status s1 on i.old_status = s1.statusid
join status s2 on i.new_status = s2.statusid

Insert into row if value exists in a different table

I have two tables:
Users:
+---------------------+---------------------+
| UserId | ValueToUpdate |
+---------------------+---------------------+
| 1 | |
| 2 | |
| 3 | |
+---------------------+---------------------+
Subscribers:
+---------------------+
| UserId |
+---------------------+
| 1 |
| 2 |
+---------------------+
I need to write a SQL query that will insert some value into the Users table, column (ValueToUpdate) if the user id in the Users table exists in the Subscribers table.
Essentially I'm looking for something like this
UPDATE Users
SET ValueToUpdate = "some value"
WHERE (UserId from the Users table exists in the Subscribers table);
How could I achieve this?
You can do this using an exists clause:
UPDATE Users u
SET ValueToUpdate = 'some value'
WHERE EXISTS (SELECT 1 FROM Subscribers S WHERE s.userid = u.userid);

removing rows from a SELECT based on columns in a different table

I'm pretty much looking for a way to filter out rows from a SELECT of one table based on certain values in rows of another table.
I'm experimenting with the example structure below. I've got a table of blog post content (one row per blog post), and another table of metadata about the posts (one row per key-value pair; each row with a column associating it with a blog post; many rows per blog post). I want to pull a row of posts only if there exists no rows in metadata where metadata.pid=posts.pid AND metadata.k='optout'. That is, for the example structure below, I just want to get back the posts.id=1 row.
(Based on what I've tried) JOINs don't end up removing the posts which have some metadata where metadata.k='optout', because the other row of metadata for that pid means it makes it into the results.
mysql> select * from posts;
+-----+-------+--------------+
| pid | title | content |
+-----+-------+--------------+
| 1 | Foo | Some content |
| 2 | Bar | More content |
| 3 | Baz | Something |
+-----+-------+--------------+
3 rows in set (0.00 sec)
mysql> select * from metadata;
+------+-----+--------+-----------+
| mdid | pid | k | v |
+------+-----+--------+-----------+
| 1 | 1 | date | yesterday |
| 2 | 1 | thumb | img.jpg |
| 3 | 2 | date | today |
| 4 | 2 | optout | true |
| 5 | 3 | date | tomorrow |
| 6 | 3 | optout | true |
+------+-----+--------+-----------+
6 rows in set (0.00 sec)
A subquery can give me the inverse of what I want:
mysql> select posts.* from posts where pid = any (select pid from metadata where k = 'optout');
+-----+-------+--------------+
| pid | title | content |
+-----+-------+--------------+
| 2 | Bar | More content |
| 3 | Baz | Something |
+-----+-------+--------------+
2 rows in set (0.00 sec)
...but using pid != any (...) gives me all 3 of the rows in posts, cause every single pid has a metadata row where k!='optout'.
Sounds like you want to do a LEFT JOIN and then check for results in which the value of the joined table is NULL, indicating that no such joined record exists.
For example:
SELECT * FROM posts
LEFT JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = 'optout')
WHERE metadata.mdid IS NULL;
This will select any row from the table posts for which no corresponding metadata row exists with a value of k = 'optout'.
edit: Worth noting that this is a key property of a left join and would not work with a regular join; a left join will always return values from the first table, even if no matching values exist in the joined table(s), allowing you to perform selections based on the absence of those rows.
edit 2: Let's clarify what's happening here with respect to the LEFT JOIN versus the JOIN (which I refer to as an INNER JOIN for clarity but is interchangable in MySQL).
Suppose you run either of these two queries:
SELECT posts.*, metadata.mdid, metadata.k, metadata.v
FROM posts
INNER JOIN metadata ON posts.pid = metadata.pid;
or
SELECT posts.*, metadata.mdid, metadata.k, metadata.v
FROM posts
LEFT JOIN metadata ON posts.pid = metadata.pid;
Both queries produce the following result set:
+-----+-------+--------------+------+-------+-----------+
| pid | title | content | mdid | k | v |
+-----+-------+--------------+------+-------+-----------+
| 1 | Foo | Some content | 1 | date | yesterday |
| 1 | Foo | Some content | 2 | thumb | img.jpg |
+-----+-------+--------------+------+-------+-----------+
Now, let's suppose we modify the query to add the extra criteria for "optout" that was mentioned. First, the INNER JOIN:
SELECT posts.*, metadata.mdid, metadata.k, metadata.v
FROM posts
INNER JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = "optout");
As expected, this returns no results:
Empty set (0.00 sec)
Now, changing that to a LEFT JOIN:
SELECT posts.*, metadata.mdid, metadata.k, metadata.v
FROM posts
LEFT JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = "optout");
This DOES produce a result set:
+-----+-------+--------------+------+------+------+
| pid | title | content | mdid | k | v |
+-----+-------+--------------+------+------+------+
| 1 | Foo | Some content | NULL | NULL | NULL |
+-----+-------+--------------+------+------+------+
The difference between an INNER JOIN and a LEFT JOIN is that an INNER JOIN will only return a result if rows from BOTH joined tables match. In a LEFT JOIN, matching rows from the first table will ALWAYS be returned, regardless of whether anything is found to join to. In a lot of cases it doesn't matter which one you use, but it's important to choose the right one so as not to get unexpected results down the line.
So in this case, the suggested query of:
SELECT posts.*, metadata.mdid, metadata.k, metadata.v
LEFT JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = 'optout')
WHERE metadata.mdid IS NULL;
Will return the same result set as above:
+-----+-------+--------------+------+------+------+
| pid | title | content | mdid | k | v |
+-----+-------+--------------+------+------+------+
| 1 | Foo | Some content | NULL | NULL | NULL |
+-----+-------+--------------+------+------+------+
Hopefully that clears it up! Joins are a great thing to learn about, having a full understanding of when to use which one is a very good thing.
You can try something like
select p.*
from posts p
where NOT EXISTS (
select pid
from metadata
where k = 'optout'
and pid = p.pid
)