Select on records based on two column criterias - sql

I would like to do an SQL query to select from the following table:
id type num
1 a 3
1 b 4
2 a 5
2 c 6
In the case where they have the same 'id' and be type 'a or b', so the result would look something like this:
id type num
1 a 3
1 b 4
Any one has any idea how that can be accomplished?

SELECT table1.*
FROM table1,
(
SELECT COUNT(*) as cnt, id
FROM (
SELECT *
FROM table1
WHERE type = 'a' OR type = 'b'
) sub1
GROUP BY id
HAVING cnt > 1
)sub2
WHERE table1.id = sub2.id
Tested here: http://sqlfiddle.com/#!2/4a031/1 seems to work fine.

Method 1:
select a.*
from some_table t
join some_table a on a.id = t.id and a.type = 'a'
join some_table b on b.id = t.id and b.type = 'b'
Method 2:
select *
from some_table t
where exists ( select *
from some_table x
where x.id = t.id
and x.type = 'a'
)
and exists ( select *
from some_table x
where x.id = t.id
and x.type = 'b'
)
The first technique offers the possibilities of duplicate rows in the results set, depending on the cardinality of id and type. The latter is guaranteed to provide a proper subset of the table.
Either query, assuming you have reasonable indices defined on the table should provide pretty equivalent performance.

Related

How to select rows that have certain values present in another column

I have a table :
id
value
1
A
1
B
1
C
2
A
2
B
3
A
my goal is to have the table where I have only IDs that have A,B,C present per id,
in this case it is:
id
1
how to construct the SQL query for that ?
One canonical approach uses aggregation:
SELECT id
FROM yourTable
WHERE value IN ('A', 'B', 'C')
GROUP BY id
HAVING COUNT(DISTINCT value) = 3;
To use exists statement like this:
select id from ${table} a where value = 'A'
and exists (select 1 from ${table} b where a.id = b.id and b.value = 'B')
and exists (select 1 from ${table} c where a.id = c.id and b.value = 'C')
To create index on column id will be more nice.

How to select groups which has only the values we want and not select it if it also has other values in SQL

id
code
value
A
cod
2
A
buy
34
A
cod
4
B
cod
44
B
F
23
C
thk
45
C
cod
33
C
F
31
D
cod
22
In this table for example, I want those groups of id which has 'code' column value as ONLY cod or F. so query should return values of id = B and nothing else. ( Not even values with id = C because id=C also has 'thk' in code , not even id= D, and output should have ids with ONLY the mentioned two values)
expected output
id
code
value
B
cod
44
B
F
23
You want all rows for the ID of which not exists a forbidden row:
select id, code, value
from mytable
where not exists
(
select null
from mytable forbidden_row
where forbidden_row.id = mytable.id
and forbidden_row.code not in ('cod', 'F')
);
One of approaches with nested query
SELECT ID,Code, value FROM (
select ID, Code,
(SELECT count(*) FROM TableA a where Code = 'cod' and a.ID = TableA.ID) Cod,
(SELECT count(*) FROM TableA a where Code = 'F' and a.ID = TableA.ID) F,
(SELECT count(*) FROM TableA a where Code not in ('F','cod') and a.ID = TableA.ID) Other,
Value
from TableA
) SOURCE
WHERE Cod <> 0 AND F <> 0 and Other = 0
We can achieve this using CTE. Check this,
-- Split the two record category first, then check cod Or F condition.
WITH Count2 AS (
SELECT id
FROM YourTable
GROUP BY id
HAVING COUNT(id) = 2
),
codORF AS (
SELECT id, code, COUNT(id) FROM YourTable T1
LEFT JOIN Count2 T2 On T1.id = T2.id
WHERE code = 'cod' OR code = 'F'
GROUP BY id, code
Having COUNT(id) = 1
)
-- Finally to take all values
SELECT T1.*
FROM YourTable T1
INNER JOIN codORF T2 ON T1.id = T2.id
with main as (
select *, count(id) over(partition by id order by id) as total_rows
from sample
), next_and_before as (
select *,
COALESCE(lag(code) over(partition by id order by id),lead(code) over(partition by id order by id)) as before_next
from main where total_rows <= 2
)
select * from next_and_before
where lower(trim(concat(code,before_next)))in('codf','fcod','cod','f')
Its a bit of hacky solution:
first you are filtering out all the rows that have less than or equal to 2 rows, since there could be cases where you only have one row per id with a code value = 'f' or 'cod', if you don't want that then simply change the last part to: in ('codf','fcod')
then out of two rows, you are looking at the next and before value and checking if it contains other than 'f' or 'cod'
where clause will filter those out if they exist
Test Results from the link below:
Results of sample data

SQL AND to see if two flags are set in two different tables?

SELECT 1 FROM
(SELECT 1 FROM mytable1 WHERE parentid = 'ID1' AND flag = 'Y') as X,
(SELECT 1 FROM mytable2 WHERE id = 'ID2' AND flag = 'Y') as Y
I'm making a query to see if two flags are set in two tables, where 'parentid' and 'id' are both primary keys. The query should return a row only if both flags are set to 'Y', or return nothing otherwise, then I do stuff with that result in my backend code.
I've tested this and it works but I feel like it looks wonky and could be optimized. Any ideas?
To get what You want:
SELECT 1
FROM mytable1 AS a, mytable2 AS b
WHERE a.parentid = 'ID1' AND a.flag = 'Y'
AND b.id = 'ID2' AND b.flag = 'Y'
But in fact, I would prefer a query with LEFT JOIN, which always gives one row, like this:
SELECT CASE WHEN a.flag = 'Y' AND b.flag = 'Y' THEN 1 ELSE 0 END AS result
FROM TABLE ( VALUES 1 ) AS always(present)
LEFT JOIN mytable1 AS a ON a.parentid = 'ID1'
LEFT JOIN mytable2 AS b ON b.id = 'ID2'
Your query is fine (although I would use CROSS JOIN. However, I would prefer a row with a specific value. I would phrase that as:
SELECT (CASE WHEN EXISTS (SELECT 1 FROM mytable1 WHERE parentid = 'ID1' AND flag = 'Y') AND
EXISTS (SELECT 1 FROM mytable2 WHERE id = 'ID2' AND flag = 'Y')
THEN 1 ELSE 0
END) as flag
You may need from dual, depending on your database.
It better to use JOIN instead of doing subqueries
SELECT mytable1.parentid, mytable2.id
FROM mytable1
JOIN mytable2 ON mytable2.flag = "Y" AND mytable1.flag = "Y"

Grouping of column

Group Error
1 a
1 b
1 c
2 a
2 b
3 a
I want write an SQL query to get that record only which has either only a or b as an error or both a and b
Output should be Group 2, 3 as
Group 2 contains both a and b , Group 3 contains only a.
Any Group that contains Error apart from a,b should not be returned.
I'd group and use a condition on a count of errors that aren't a or b:
SELECT [group] -- Assuming MS SQL Syntax, like Ross Presser did in his answer
FROM mytable
GROUP BY [group]
HAVING COUNT(CASE WHEN [error] NOT IN ('a', 'b') THEN 1 END) = 0
Unsure of the DBMS and so the following may not be applicable, but to offer another option using a correlated subquery:
SELECT DISTINCT a.Group
FROM Table1 a
WHERE NOT EXISTS (SELECT 1 FROM Table1 b WHERE a.Group = b.Group AND b.Error NOT IN ('a','b'))
Or using a LEFT JOIN on a query of Groups containing at least one Error code not equal to a or b:
SELECT DISTINCT a.Group
FROM
Table1 a LEFT JOIN
(
SELECT DISTINCT t.Group
FROM Table1 t
WHERE t.Error <> 'a' AND t.Error <> 'b'
) b
ON a.Group = b.Group
WHERE b.Group IS NULL
And one more very specific to your example, but just for fun -
SELECT t.Group
FROM Table1 t
GROUP BY t.Group
HAVING MIN(t.Error) >= 'a' AND MAX(t.Error) <= 'b'
Replace Table1 with the name of your table in all of the above.

How to find unmatched records in a single table?

I'm scraping a log file for transaction records that I am inserting into a table that will be used for several mining tasks. Each record has (among other things) an ID and a transaction type, either request or response. A request/response pair will have the same ID.
One of my tasks is to find all of the requests that do not have a corresponding response. I thought about joining the table to itself, where A.ID = B.ID AND A.type = 'req' and B.type = 'res', but that gives me the opposite of what I need.
Since the IDs will always occur either once or twice, is there a query that would select ID where there is only one occurrence of that ID in the table?
This is a very common type of query. You can try aggregating over the ID values in your table using GROUP BY, then retaining those ID which appear only once.
SELECT ID
FROM yourTable
GROUP BY ID
HAVING COUNT(*) = 1
If you also want to return the entire records for those ID occurring only once, you could try this:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT ID FROM yourTable GROUP BY ID HAVING COUNT(*) = 1
) t2
ON t1.ID = t2.ID
The straight-forward way is NOT IN:
select *
from mytable
where type = 'req'
and id not in (select id from mytable where type = 'res');
You can write about the same with NOT EXISTS, but the query becomes slightly less readable:
select *
from mytable req
where type = 'req'
and not exists (select * from mytable res where type = 'res' and res.id = req.id);
And then there are forms of aggregation you can use, e.g.:
select *
from mytable
where type = 'req'
and id in
(
select id
from mytable
group by id
having count(case when type = 'res' then 1 end) = 0
);
This will give you the ones that have Request but not respose
SELECT *
FROM your_table A LEFT OUTER JOIN
your_table B ON A.ID = B.ID
AND A.type = 'req' and B.type = 'res'
WHERE B.ID IS NULL