Joining two tables with condition - sql

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'

Related

Delete rows from table using JOIN - SQL Server

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);

Update unique table SQL

Hello i have this table.
ADDR | STATE | ID
CRA.20 15 | REP | (null)
CRA.20 15 | REP | (null)
CRA.20 15 | REP | (null)
CRA.20 15 | PRI | RR_88_JK
I need transform this table ..
ADDR | STATE | ID
CRA.20 15 | REP | RR_88_JK
CRA.20 15 | REP | RR_88_JK
CRA.20 15 | REP | RR_88_JK
CRA.20 15 | PRI | RR_88_JK
I have this QRY but not function. You have a one idea?.
UPDATE TABLE_A A
SET a.ID = b.ID
WHERE EXISTS
(SELECT b.ID
FROM TABLE_A B
WHERE a.ADDR = B.ADDR AND b.STATE = 'PRI')
and A.STATE = 'REP';
Thanks.
You need a subquery to set the value:
UPDATE TABLE_A A
SET a.ID = (SELECT b.ID
FROM TABLE_A B
WHERE a.ADDR = B.ADDR AND b.STATE = 'PRI' AND rownum = 1
)
WHERE EXISTS (SELECT b.ID
FROM TABLE_A B
WHERE a.ADDR = B.ADDR AND b.STATE = 'PRI'
) AND
A.STATE = 'REP';
B is only known in the subquery, not in the outer query.
The following will do what you want:
UPDATE TABLE_A A
SET a.ID = (SELECT MIN(ID)
FROM TABLE_A
WHERE ID IS NOT NULL AND
STATE = 'PRI')
WHERE ID IS NULL AND
STATE = 'REP'
Best of luck.

ORACLE - remove from result set if two or more "same" records found

ID | NAME | REFERENCE | STATUS
-------------------------------
1 | name1 | 123 | 0
2 | name1 | 123 | 1
3 | name2 | 111 | 0
4 | name3 | 222 | 0
5 | name5 | 555 | 1
Let's say I have this table TABLE1. I need a select statement that will only find records with STATUS=0, but if "same" record exists with STATUS=1 (like those records with ID 1 and 2)
So, query must find only third and fourth record.
Based on your suggested results, I am reading your question as "find only records with status = 0 where the same name/reference combination doesn't have a record with status = 1".
One way you can do this is with a not exists clause:
select t.*
from table t
where status = 0 and
not exists (select 1
from table t2
where t2.name = t.name and
t2.reference = t.reference and
t2.status = 1
);
Try this query out:
SELECT A.*
FROM TABLE1 A
LEFT JOIN TABLE1 B ON
A.ID = B.ID AND
A.NAME = B.NAME AND
A.REFERENCE = B.REFERENCE AND
B.STATUS = 1
WHERE A.STATUS = 0
AND B.REFERENCE IS NULL
The NULL check on B.REFERENCE in the WHERE clause basically ensures that there was no matching record found in the LEFT JOIN (you could just use B.ID or B.NAME also).
MINUS can be used to get the value with Status both 0 but not 1.
SELECT ID, Name, Reference, Status
FROM Table1
WHERE (Name, Reference) IN (SELECT Name, Reference
FROM Table1
WHERE Status = 0
MINUS
SELECT Name, Reference
FROM Table1
WHERE Status = 1)
SELECT * FROM TABLE WHERE NAME NOT IN
(
SELECT NAME FROM
TABLE
GROUP BY NAME
HAVING COUNT(DISTINCT STATUS)>1
) AND STATUS='0';

SQL query uses "wrong" join

I have an query which gives me the wrong result.
Tables:
A
+----+
| id |
+----+
| 1 |
| 2 |
+----+
B
+----+----+
| id | x | B.id = A.id
+----+----+
| 1 | 1 |
| 1 | 1 |
| 1 | 0 |
+----+----+
C
+----+----+
| id | y | C.id = A.id
+----+----+
| 1 | 1 |
| 1 | 2 |
+----+----+
What I want to do: Select all rows from A. For each row in A count in B all x with value 1 and all x with value 0 with B.id = A.id. For each row in A get the minimum y from C with C.id = A.id.
The result I am expecting is:
+----+------+--------+---------+
| id | min | count1 | count 2 |
+----+------+--------+---------+
| 1 | 1 | 2 | 1 |
| 2 | NULL | 0 | 0 |
+----+------+--------+---------+
First Try:
This doesn't work.
SELECT a.id,
MIN(c.y),
SUM(IF(b.x = 1, 1, 0)),
SUM(IF(b.x = 0, 1, 0))
FROM a
LEFT JOIN b
ON ( a.id = b.id )
LEFT JOIN c
ON ( a.id = c.id )
GROUP BY a.id
+----+------+--------+---------+
| id | min | count1 | count 2 |
+----+------+--------+---------+
| 1 | 1 | 4 | 2 |
| 2 | NULL | 0 | 0 |
+----+------+--------+---------+
Second Try:
This works but I am sure it has a bad performance.
SELECT a.id,
MIN(c.y),
b.x,
b.y
FROM a
LEFT JOIN (SELECT b.id, SUM(IF(b.x = 1, 1, 0)) x, SUM(IF(b.x = 0, 1, 0)) y FROM b) b
ON ( a.id = b.id )
LEFT JOIN c
ON ( a.id = c.id )
GROUP BY a.id
+----+------+--------+---------+
| id | min | count1 | count 2 |
+----+------+--------+---------+
| 1 | 1 | 2 | 1 |
| 2 | NULL | 0 | 0 |
+----+------+--------+---------+
Last Try:
This works too.
SELECT x.*,
SUM(IF(b.x = 1, 1, 0)),
SUM(IF(b.x = 0, 1, 0))
FROM (SELECT a.id,
MIN(c.y)
FROM a
LEFT JOIN c
ON ( a.id = c.id )
GROUP BY a.id) x
LEFT JOIN b
ON ( b.id = x.id )
GROUP BY x.id
Now my question is: Is the last one the best choise or is there a way to write this query with just one select statement (like in the first try)?
Your joins are doing cartesian products for a given value, because there are multiple rows in each table.
You can fix this by using count(distinct) rather than sum():
SELECT a.id, MIN(c.y),
count(distinct (case when b.x = 1 then b.id end)),
count(distinct (case when b.x = 0 then b.id end))
FROM a
LEFT JOIN b
ON ( a.id = b.id )
LEFT JOIN c
ON ( a.id = c.id )
GROUP BY a.id;
You can also fix this by pre-aggregating b (and/or c). And you would need to take that approach if your aggregation function were something like the sum of a column in b.
EDIT:
You are correct. The above query counts the distinct values of B, but B contains rows that are exact duplicates. (Personally, I think having a column with the name id that has duplicates is a sign of poor design, but that is another issue.)
You could solve it by having a real id in the b table, because then the count(distinct) would count the correct values. You can also solve it by aggregating the two tables before joining them in:
SELECT a.id, c.y, x1, x0
FROM a
LEFT JOIN (select b.id,
sum(b.x = 1) as x1,
sum(b.x = 0) as x0
from b
group by b.id
) b
ON ( a.id = b.id )
LEFT JOIN (select c.id, min(c.y) as y
from c
group by c.id
) c
ON ( a.id = c.id );
Here is a SQL Fiddle for the problem.
EDIT II:
You can get it in one statement, but I'm not so sure that it would work on similar data. The idea is that you can count all the cases where x = 1 and then divide by the number of rows in the C table to get the real distinct count:
SELECT a.id, MIN(c.y),
coalesce(sum(b.x = 1), 0) / count(distinct coalesce(c.y, -1)),
coalesce(sum(b.x = 0), 0) / count(distinct coalesce(c.y, -1))
FROM a
LEFT JOIN b
ON ( a.id = b.id )
LEFT JOIN c
ON ( a.id = c.id )
GROUP BY a.id;
It is a little tricky, because you have to handle NULLs to get the right values. Note that this is counting the y value to get a distinct count from the C table. Your question re-enforces why it is a good idea to have a unique integer primary key in every table.

Sql query with many to many tables

I'm using VB.Net express with an Access file, and I have the following tables:
table Formula
id | name
-------------------
1 | formula 1
2 | formula 2
3 | formula 3
table Component
id | name
--------------------
1 | A
2 | B
3 | C
4 | D
table FormulaComponents
formula_id | component_id
-------------------------
1 | 1
1 | 2
1 | 4
2 | 1
2 | 3
2 | 4
3 | 1
3 | 2
3 | 3
So each formula have one or more components.
Which query will I use if I want all the formulas with for example Component A AND Component D (Result: formula 1, formula 2)? I try something with intersect, but it seems it doesn't work in VB...
Thanks!
Update:
select f.*
from (
select c.id
from FormulaComponents fc
inner join Component c on fc.component_id = c.id
where c.name in ('A', 'B')
group by c.id
having count(distinct c.name) = 2
) c2
inner join FormulaComponents fc on c2.id = fc.component_id
inner join Formula f on fc.formula_id = f.id
SELECT DISTINCT
f.*
FROM Formula f
INNER JOIN FormulaComponent fc on fc.formula_id = f.formula_id
INNER JOIN Component c on c.component_id = fc.componentid
WHERE Exists (SELECT * FROM FormulaComponent fc1 WHERE fc1.formulaID = f.formulaId AND c.Name = 'A')
AND Exists (SELECT * FROM FormulaComponent fc1 WHERE fc1.formulaID = f.formulaId AND c.Name = 'D')