How to get an ID associated with at least all contents? - sql

Suppose we have the database:
-----------
| A -|- B |
|----|----|
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
-----------
Where A and B is the primary key. Suppose we want to get all As that contain the elements in B of 1 and 2.
SELECT A FROM Table WHERE B = 1 AND B = 2;
The above fails because it never holds true as the query is only for a single record.
SELECT A FROM Table WHERE B = 1 OR B = 2;
Works but erroneously includes the primary key value 2, which only maps to 1 in B, and not both 1 and 2 in B.

GROUP BY solution, return all a's that have more than 1 different b value in (1,2):
select a from table
where b in (1,2)
group by a
having count(distinct b) > 1
Or, JOIN solution:
select distinct a
from (select a from table where b = 1) t1
join (select a from table where b = 2) t2
on t1.a = t2.a
Or an INTERSECT solution:
select a from table where b = 1
intersect
select a from table where b = 2
Edit:
GROUP BY query that perhaps is faster then the HAVING count distinct version:
select a from table
where b in (1,2)
group by a
having max(b) <> min(b)

You can use the group by method from jarlh or make a Join with a 'distinct':
select distinct a
from (select a from table where b = 1) t1
join (select a from table where b = 2) t2
on t1.a = t2.a

Something like this (assuming that you need to filter by the specific IDs in B.
SELECT DISTINCT A
FROM Table AS T
WHERE EXISTS (SELECT 1 from Table WHERE Table.A = T.A and B = 1)
AND EXISTS (SELECT 1 from Table WHERE Table.A = T.A and B = 2)

Try this
SELECT A
FROM Table
WHERE EXISTS (
SELECT 1
FROM Table t1
WHERE t1.A = Table.A
AND t1.B = 1
)
AND EXISTS (
SELECT 1
FROM Table t2
WHERE t2.A = Table.A
AND t2.B = 2
)

A cannot be the primary key here, since the column contains duplicates.
One possible solution:
SELECT * FROM (SELECT A, group_concat(B, ',') AS C FROM tab GROUP BY A) s WHERE s.C = "1,2";

Related

Displaying rows in which the column value does not appear in the same group of another column

I am trying to write the right query, but they show incorrect results.
For example, I have table ABC:
id a_id b_id c
1 10 100
2 10 111
3 11 111
4 11 222
5 11 333 111&&222&&333
6 12 444
7 12 555 444&&111
In column c there can only be b_id with && that belong to the same a_id.
For example, this record is incorrect:
7 12 555 444&111
because 111 is in a_id 10 or 11 not 12. How can I find it?
I need to find all rows where occur (invalid) values b_id in column C that are not in the same a_id.
The Sql(postgresql) doesn't work properly, why? Thanks for any help.
My sql:
SELECT * from ABC x
WHERE x.id IN (SELECT y.id
FROM ABC y
WHERE x.a_id = y.a_ai AND y.c NOT LIKE '%'||x.b_id||'%'
Use regexp_split_to_table() to split the delimited string into rows. Then use an anti-join method, to find not matching entries:
with t1 as (
select *, regexp_split_to_table(c, '&&')::int AS split_c
from abc
where c <> ''
)
select distinct t1.id, t1.a_id, t1.b_id, t1.c
from t1
left join abc t2
on t2.a_id = t1.a_id
and t2.b_id = t1.split_c
where t2.a_id is null
See demo on db-fiddle.com
Instead of an anti-join, you can also use a NOT EXISTS subquery:
with t1 as (
select *, regexp_split_to_table(c, '&&')::int AS split_c
from abc
where c <> ''
)
select distinct t1.id, t1.a_id, t1.b_id, t1.c
from t1
where not exists (
select *
from abc t2
where t2.a_id = t1.a_id
and t2.b_id = t1.split_c
)
If you also want to know, which value in c is wrong, replace select distinct ... with select t1.*. You will find the "wrong" value in split_c column. Or use GROUP BY and array_agg() to list all wrong values in a single row:
with t1 as (
select *, regexp_split_to_table(c, '&&')::int AS split_c
from abc
where c <> ''
)
select t1.id, t1.a_id, t1.b_id, t1.c, array_agg(t1.split_c) as wrong_c
from t1
left join abc t2
on t2.a_id = t1.a_id
and t2.b_id = t1.split_c
where t2.a_id is null
group by t1.id, t1.a_id, t1.b_id, t1.c
Assuming b_id is unique in the table, you can use:
select abc.*
from abc
where exists (select 1
from abc abc2
where abc2.a_id = abc.a_id and
concat('&&', abc2.c, '&&') not like concat('%&&', abc.b_id, '&&%')
);

Sum query with reference to different coloumn for each row?

Is it possible to carry out a sum query where the row for each part of the sum is determine from a join?
For example if I have tables
table A
id | value
1 | 10
2 | 15
3 | 10
And
table b
id | b | c
1 | 2 | 3
2 | 1 | 2
Is it possible to do a SUM(tableA.value * tableB.<specific_column>) Where either the SUM is carried out directly as a join or the join table is prequired from a specification, for sake of argument, a string "bcb"?
Edit:
The end result I'm hoping to achieve would be equivalent to this:
SUM(SELECT value * b FROM tableA a JOIN tableB b ON b.id = 1 WHERE a.id = 1,
SELECT value * c FROM tableA a JOIN tableB b ON b.id = 1 WHERE a.id = 2,
SELECT value * b FROM tableA a JOIN tableB b ON b.id = 2 WHERE a.id = 3);
I guess there's two parts to this: A simple join of A and selected values from B such that B is reduced to a single selectValue column.
Thanks.
As asked in comment it should be better to show us what output you really wants, but as I understand you wants to do something like :
SELECT id, SUM(a.value * b.b)
FROM a JOIN b USING(id)
GROUP BY id;
It's what you want ? I do not really understand you "bcb" point ...
Not because in your comment you said SUM(value, value, value) and I think you want to add those values so, I'll do something like this :
WITH
sum1 AS (SELECT value * b AS res
FROM tableA a
JOIN tableB b ON b.id = 1
WHERE a.id = 1),
sum2 AS (SELECT value * c AS res
FROM tableA a
JOIN tableB b ON b.id = 1
WHERE a.id = 2),
sum3 AS (SELECT value * b AS res
FROM tableA a
JOIN tableB b ON b.id = 2
WHERE a.id = 3)
SELECT SUM(sum1.res + sum2.res + sum3.res)
FROM sum1, sum2, sum3;
I've tested #Hervé Piedvache's code and it returns NULL, because SELECT value * b AS val FROM tableA a JOIN tableB b ON b.id = 1 WHERE a.id = 1 has two rows. A work around would be:
SELECT SUM(val) FROM
(SELECT value * b AS val FROM tableA a JOIN tableB b ON b.id = 1 WHERE a.id = 1
UNION
SELECT value * c AS val FROM tableA a JOIN tableB b ON b.id = 1 WHERE a.id = 2
UNION
SELECT value * b AS val FROM tableA a JOIN tableB b ON b.id = 2 WHERE a.id = 3) data;

TSQL - Select rows with same column A but different column B

I'm trying to find rows (Name) that does not have ID = 1. For example, if my table looked like this:
Name ID
--------------
A 1
A 0
B 1
B 0
C 0
D 2
D 0
The answer to this query would be:
Name
-----
C
D
Do you have any idea?
SELECT Name
FROM myTable
GROUP BY Name
HAVING SUM(CASE WHEN ID = 1 THEN 1 ELSE 0 END) = 0
Here is one way to do it:
SELECT DISTINCT Name
FROM Table t0
WHERE NOT EXISTS
(
SELECT 1
FROM Table t1
WHERE t0.Name = t1.Name
AND t1.Id = 1
)
Try this query:
SELECT DISTINCT(name)
FROM tbl t1
WHERE
NOT EXISTS (SELECT name FROM tbl t2 WHERE ID=1 AND t1.name=t2.name)
Select Distinct name
From myTable
Where name not in (Select name From myTable Where id= 1)

SQL: How do I combine tables on a single but non-unique identifier?

I have two tables:
TABLE 1
ID Value ValueFromTable2
1 A NULL
1 B NULL
1 C NULL
1 D NULL
2 E NULL
2 F NULL
TABLE 2
ID Value
1 A1
1 A2
1 A3
2 BOB
2 JIM
I would like to update TABLE 1 with the values of TABLE 2 such that the following rows would result:
TABLE 1
ID Value ValueFromTable2
1 A A1
1 B A2
1 C A3
1 D NULL
2 E BOB
2 F JIM
Order it not terribly important. That is, I'm not concerned that A be paired with A1 or that B be paired with A2. I just need a full set of data from the Value column in Table 2 to be available from Table 1.
Please advise!
You need a key for joining them. The implicit key is the ordering. You can add that in explicitly, using row_number():
select coalesce(t1.id, t2.id) as id,
t1.value, t2.value
from (select t1.*, row_number() over (partition by id order by (select NULL)) as seqnum
from table1 t1
) t1 full outer join
(select t2.*, row_number() over (partition by id order by (select NULL)) as seqnum
from table2 t2
) t2
on t1.id = t2.id and t1.seqnum = t2.seqnum;
By using full outer join, all values will appear, regardless of which is the longer list.

SQL aggregation query, grouping by entries in junction table

I have TableA in a many-to-many relationship with TableC via TableB. That is,
TableA TableB TableC
id | val fkeyA | fkeyC id | data
I wish the do select sum(val) on TableA, grouping by the relationship(s) to TableC. Every entry in TableA has at least one relationship with TableC. For example,
TableA
1 | 25
2 | 30
3 | 50
TableB
1 | 1
1 | 2
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2
should output
75
30
since rows 1 and 3 in Table have the same relationships to TableC, but row 2 in TableA has a different relationship to TableC.
How can I write a SQL query for this?
SELECT
sum(tableA.val) as sumVal,
tableC.data
FROM
tableA
inner join tableB ON tableA.id = tableB.fkeyA
INNER JOIN tableC ON tableB.fkeyC = tableC.id
GROUP by tableC.data
edit
Ah ha - I now see what you're getting at. Let me try again:
SELECT
sum(val) as sumVal,
tableCGroup
FROM
(
SELECT
tableA.val,
(
SELECT cast(tableB.fkeyC as varchar) + ','
FROM tableB WHERE tableB.fKeyA = tableA.id
ORDER BY tableB.fkeyC
FOR XML PATH('')
) as tableCGroup
FROM
tableA
) tmp
GROUP BY
tableCGroup
Hm, in MySQL it could be written like this:
SELECT
SUM(val) AS sumVal
FROM
( SELECT
fkeyA
, GROUP_CONCAT(fkeyC ORDER BY fkeyC) AS grpC
FROM
TableB
GROUP BY
fkeyA
) AS g
JOIN
TableA a
ON a.id = g.fkeyA
GROUP BY
grpC
SELECT sum(a.val)
FROM tablea a
INNER JOIN tableb b ON (b.fKeyA = a.id)
GROUP BY b.fKeyC
It seems that is it needed to create a key_list in orther to allow group by:
75 -> key list = "1 2"
30 -> key list = "1 2 3"
Because GROUP_CONCAT don't exists in T-SQL:
WITH CTE ( Id, key_list )
AS ( SELECT TableA.id, CAST( '' AS VARCHAR(8000) )
FROM TableA
GROUP BY TableA.id
UNION ALL
SELECT TableA.id, CAST( key_list + ' ' + str(TableB.id) AS VARCHAR(8000) )
FROM CTE c
INNER JOIN TableA A
ON c.Id = A.id
INNER join TableB B
ON B.Id = A.id
WHERE A.id > c.id --avoid infinite loop
)
Select
sum( val )
from
TableA inner join
CTE on (tableA.id = CTE.id)
group by
CTE.key_list