Delete rows from table using JOIN - SQL Server - sql

I have a table_A -
id | name | is_active |
----+---------+------------+
1 | jon | 1 |
2 | ham | 0 |
3 | gary | null |
I have a table_B -
id | name |
----+---------+
1 | jon |
2 | ham |
I want to remove rows from table B that have is_active value as 0 OR null in table A. So I'm thinking about an INNER JOIN on id column and applying a WHERE clause.
DELETE ROWS from table_B B
INNER JOIN table_A A ON B.id = A.id
WHERE A.is_active = 0 OR A.is_active IS NULL
I don't want any additional columns or changes in table B after the above query. Is this the right way to do it?

Instead of JOIN, use exists:
DELETE FROM table_B
WHERE EXISTS (SELECT 1
FROM table_A A
WHERE A.id = table_B.id AND
(A.is_active = 0 OR A.is_active is null)
);

You need to say which table you want to delete from, use the alias if you have one:
DELETE B
FROM table_B B
INNER JOIN table_A A ON B.id = A.id
WHERE (A.is_active = 0 OR A.is_active IS NULL);

Related

Trying to write an inner join to filter out some conditions

I'm currently struggling with carrying out some joins and hoping someone can shed some light on this.
I have three tables: A,B,C
Table C lists names of individuals
Table A lists the food they like to eat
Table B is the link to show what food in A a person likes from C (Our
system was built without foreign keys! I know, it's a pain!)
What I'm trying to write is a query that will return a list of values from Table C which shows the individuals that don't like a specific food...say PFC
I have the following:
select * from table_c c
inner join table_b b
on c.name = b.bValue
inner join table_a a
on b.aValue = a.number
where a.value not in('PFC')
I'm assuming the joins are working but as table A has multiple values, the two extra rows are being returned. Is it possible to not show this client if one of the joins shows a food I don't want to see?
Table A
|---------------------|------------------|
| Number | Value |
|---------------------|------------------|
| 1 | McDs |
|---------------------|------------------|
| 1 | KFC |
|---------------------|------------------|
| 1 | PFC |
|---------------------|------------------|
Table B
|---------------------|------------------|
| bValue | aValue |
|---------------------|------------------|
| John | 1 |
|---------------------|------------------|
Table C
|---------------------|
| Name |
|---------------------|
| John |
|---------------------|
I'm also using SQL Server 2013 if that makes a difference!
With NOT EXISTS:
select * from table_c c
where not exists (
select 1 from table_b b inner join table_a a
on b.aValue = a.number
where b.bValue = c.name and a.value = 'PFC'
)
One option is to aggregate by name:
SELECT
c.Name
FROM table_c c
INNER JOIN table_b b
ON c.Name = b.bValue
INNER JOIN table_a a
ON b.aValue = a.Number
GROUP BY
c.Name
HAVING
COUNT(CASE WHEN a.Value = 'PFC' THEN 1 END) = 0;
We could also try expressing this using an exists query:
SELECT
c.Name
FROM table_c c
WHERE NOT EXISTS (SELECT 1 FROM table_b b
INNER JOIN table_a a
ON b.aValue = a.Number
WHERE c.Name = b.bValue AND
a.Value = 'PFC');

Oracle - Conditional Join eliminating additional joins where unnecessary

Let’s say I have a simplified table structure as follows:
Table A - ID (PK)
Table B - ID (PK), AID = FK to Table A
Table C - ID (PK), BID = FK to Table B
Table D - ID (PK), CID = FK to Table C
Query something like so:
SELECT * FROM TABLE_A TBLA
LEFT JOIN TABLE_B TBLB ON TBLA.ID = TBLB.AID
LEFT JOIN TABLE_C TBLC ON TBLB.ID = TBLC.BID
LEFT JOIN TABLE_D TBLD ON TBLC.ID = TBLD.CID
It’s relatively straight-forward but what I want to do is somewhat a conditional join in that I want all records from TABLE A but want to join TABLE B -> TABLE C -> TABLE D if the first join between TABLE A and TABLE B is satisfied, bearing in mind that I could change TABLE B -> TABLE C -> TABLE D joins to be INNER as there’ll exist in that initial join between TABLE A and TABLE B is satisfied.-
But also I’d need a WHERE condition on TABLE D also.
So essentially want to eliminate the LEFT JOIN’S between TABLE_B, TABLE_C, TABLE_D where join isn’t satisfied between TABLE_A and TABLE_B.
Very simplified data so apologies!
| Table A |
| ID |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| Table B |
| ID | AID |
| 1 | 5 |
| Table C |
| ID | BID |
| 1 | 1 |
| Table D |
| ID | CID | Value |
| 1 | 1 | ABC |
The reason I want to eliminate the join is that for 4 of the 5 rows in Table A, I’m doing unnecessary joins across three tables to get the value in Table D.
You can use brackets with join, but not sure if that would help you!
SELECT * FROM
TABLE_A TBLA
LEFT JOIN (TABLE_B TBLB
INNER JOIN TABLE_C TBLC ON TBLB.ID = TBLC.BID
INNER JOIN TABLE_D TBLD ON TBLC.ID = TBLD.CID) ON TBLA.ID = TBLB.AID
This way, when you have A matches entries in B, but B is not matched in the chain to C and D, B data is not retrieved, and so for C
You mentioned that you need a where condition on table D? Does that mean that you always have to link to D?! Note that if you have a condition on D, the condition has to be satisfied in all cases. Hence, when no records are retrieved from D, no records will be retreived from the query (even with your initial outer join unless you used OR .. is null )

Updating FK by Child Table Field Value

Given the following records in table B, in that:
B.Id | B.CId | B.Name
1 | 1 | "apple"
2 | 2 | "apple"
3 | 1 | "banana"
4 | 2 | "banana"
In table A, I have values:
A.Id | A.BId | A.CId
1 | 1 | 2
2 | 4 | 1
which actually needs to be updated to:
A.Id | A.BId | A.CId
1 | 2 | 2
2 | 3 | 1
based on the fact that A.CId needs to use A.BId where B.CId matches, and B.Name matches the A.BId's .Name.
Try the following:
SET bid = b.id --setting the value for bid to the id value in table b
FROM b b JOIN a a on a.cid = b.cid --where the cid cols in each table match
WHERE b.id IN
(SELECT b1.id from b b1 join a a1 on a1.bid = b1.id WHERE b1.name = b.name)
--and table b's ID is for row with a name value matching when joined on the
--foreign key instead of cid
Here's a reference to sql update statements involving multiple tables:
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/1022bbce-3d99-49d1-83a1-75bafa49ac25/tsql-update-statement-with-join
Sounds like a job for update ... from ..., see UPDATE (Transact-SQL).
update
A
set
BId = B.Id
from
B
where
A.CId = B.CId
And here's a SQLFiddle as full example.

Joining two tables with condition

I have tables like this
TABLE A
id | name
1 | a
2 | b
3 | c
TABLE B
id | ida | prp | prpval
1 | 1 | visible | true
2 | 1 | active | true
3 | 2 | visible | false
4 | 2 | active | true
5 | 3 | visible | true
6 | 3 | active | true
Table A and Table B are connected by id-ida.
I want to display all names(name) from Table A that have property visible(prp) from TABLE B set to 'true' (prpval). Without any duplicates. So in this example i want to display 'a' and 'b'. How to do it?
select a.name
from a
join b on a.id = b.aid
group by a.name
having sum(case when prp = 'visible' and prpvalue = 'true' then 1 else 0 end) > 0
A simple answer would be:
SELECT A.name
FROM A
INNER JOIN B
ON A.id = B.ida
WHERE B.prp = 'visible'
AND B.prpval = 'true'
SELECT DISTINCT a.name
FROM A a join B b on a.id = b.ida
WHERE b.prp = 'visible' and b.prpval = 'true'
Because you don't want duplicates, I would approach this using an exists clause:
select *
from tableA a
where exists (select 1
from tableB b
where b.ida = a.id and b.prp = 'visitor' and b.prpval = 'true'
);
For performance, create an index on tableB(ida, prp, prpval).
select a.name
from A as a
join B as b on a.id=b.ida
and b.prpval='True' and b.prp='visible'

SQL Join to Get Row with Maximum Value from Right table

I am having problem with sql join (oracle/ms sql)
I have two tables
A
ID | B_ID
---|------
1 | 1
1 | 4
2 | 3
2 | 2
----------
B
B_ID | B_VA| B_VB
-------|--------|-------
1 | 1 | a
2 | 2 | b
3 | 5 | c
4 | 2 | d
-----------------------
From these two tables I need A.ID, B.B_ID, B.B_VA (MAX), B.B_VB (with max B.B_VA)
So result table would be like
ID | B_ID | B_VA| B_VB
-------|--------|--------|-------
1 | 4 | 2 | d
2 | 3 | 5 | c
I tried some joins without success. Can anyone help me with query to get the result I want.
Thank you
Your logic as described doesn't quite correspond to the data. For instance, b_va is numeric, but the column in the output is a string.
Perhaps you want this. The data in a to be aggregated to get the maximum b_id value. Then each column to be joined to get the corresponding b_vb column. That, at least, conforms to your desired output:
select a.id, a.b_id, b1.b_vb as b_va, b2.b_vb
from (select id, max(b_id) as b_id
from a
group by id
) a join
b b1
on a.id = b1.b_id join
b b2
on a.b_id = b2.b_id;
EDIT:
For the corrected data, I think this is what you want:
select a.id, a.b_id, max(b1.b_va) as b_va, b2.b_vb
from (select id, max(b_id) as b_id
from a
group by id
) a join
b b1
on a.id = b1.b_id join
b b2
on a.b_id = b2.b_id
group by a.id, a.b_id, b2.b_vb;
Try this
SELECT X.ID, Y.B_ID, X.B_VA, Y.B_VB
FROM (SELECT A.ID, MAX(B_VA) AS B_VA
FROM A INNER JOIN B ON A.B_ID = B.B_ID
GROUP BY A.ID) AS X INNER JOIN
A AS Z ON X.ID = Z.ID INNER JOIN
B AS Y ON Z.B_ID=Y.B_ID AND X.B_VA=Y.B_VA