SQL check adjacent rows for sequence - sql

I have a table with an id column (unique, primary), a name (not unique--in fact, most likely repeated), and a flag column which has values 0, 1, or 2. Let's say I reorder the table using the command
SELECT id, name, flag ORDER BY name, id
I want to produce using SQL a list of names where, when the rows in the reordering are read downward, there are two adjacent rows with the same name and flags of value 0 and 1 (in that order). Additionally, in that list, I want to have the ids of the two rows where this happened. If it happened more than once, there should be multiple rows.
So, for instance, in the following
id name flag
4 Bob 0
5 Bob 2
6 Bob 1
1 Cathy 0
7 Cathy 1
3 David 0
2 Elvis 2
8 Elvis 0
9 Elvis 1
I would want to select
name id1 id2
Cathy 1 7
Elvis 8 9
How do I do this?
I'm using MySQL.
EDIT: Note that the IDs for those adjacent rows might not be consecutive; they're only consecutive if we order by name. See, for example, Cathy.
Thanks!

try
select t1.name, t1.id as id1,t2.id as id2
from tablename t1, tablename t2
where t1.flag = 0 and t2.id = t1.id+1 and t2.flag = 1 and t1.name = t2.name

Try:
select t1.name, t1.id as id1,t2.id as id2
from tablename t1
join tablename t2
on t2.name = t1.name and t2.flag = t1.flag + 1 and t2.id =
(select min(t3.id) from tablename t3
where t1.name = t3.name and t1.id < t3.id)
EDIT: amended join to t2 to include lookup on subquery

Related

If I left join table2 to table1, how I can I call on the IDs in table1 that don't appear in table2 within a CASE or IF Statement?

I am writing an IF/Case statement that requires me to identify all the Ids from and ID column in Table1 that don't appear in a 2nd table Table2 which is left joined on to Table1 on the ID Column, And based on that IF statement I would like to produce a binary column called Missing with 1s, 0s.
Table1
ID
Region
a
US
b
US
c
Mexico
d
Japan
Table2
ID
Years
a
5
d
10
After joining this is what I have:
ID
Region
Years
a
US
5
b
US
null
c
Mexico
null
d
Japan
10
The final outcome should be:
ID
Region
Years
Missing
a
US
5
0
b
US
null
1
c
Mexico
null
1
d
Japan
10
0
I don't know how to Identify those specific Ids in the IF or CASE statement but the rest of the query I can write. I tried to write
IF(Table1.ID NOT IN Table2.ID, 1, 0) As Missing
but that did not work (some sort of unnest issue)
You may try:
SELECT t1.*, t2.ID IS NULL AS Missing
FROM Table1 t1
LEFT JOIN Table2 t2
ON t2.ID = t1.ID;
Using the IF() function we can try:
SELECT t1.*, IF(t2.ID IS NULL, 1, 0) AS Missing
FROM Table1 t1
LEFT JOIN Table2 t2
ON t2.ID = t1.ID;

SQL Subtract corresponding elements in separate tables

So i have 2 tables, both share the key "Product ID", i need to subtract where they match, for example
Table 1
Key Value
1 70
2 50
3 12
4 5
5 18
Table 2
Key Value
2 5
3 3
4 1
5 1
and i need the output
Output
Key Value
1 70
2 45
3 9
4 4
5 17
i tried
Update Table1
Set Table1.Count = Table1.Count - (
Select Table2.Count
From Table2
Where Table2.ID = Table2.ID
);
But this sets the value of Key 1 to null
I also tried putting a join before the Where, but that gave me the error:
ORA-01427: single-row subquery returns more than one row ORA-06512: at "SYS.DBMS_SQL"`
Your error message indicates that you are using Oracle, not MySQL as you tagged.
Problems with your query:
the correlation clause in the suqbuery is wrong: Table2.ID = Table2.ID succeds for all rows in table2, and hence returns more than one row, hence the error that you are getting
you need to handle the case when the subquery returns no rows, otherwise the null value propagates to your update: coalesce() can be used for this
Consider:
update table1
set count = count - coalesce(
(select t2.count from table2 t2 where t2.id = table1.id),
0
);
Or, alternatively:
update table1
set count = (select t2.count from table2 t2 where t2.id = table1.id)
where exists (select 1 from table2 t2 where t2.id = table1.id)
You can use LEFT JOIN including COALESCE function in order to update non-matching records along with preventing to have null values :
UPDATE Table1 t1
LEFT JOIN Table2 t2 ON t1.Id = t2.Id
SET t1.Count = Coalesce(t1.Count,0) - Coalesce(t2.Count,0)
Demo for MySQL Case
Update : seems that question is retagged Oracle. Then you can use such a MERGE Statement :
MERGE INTO Table1 t1
USING Table2 t2
ON (t1.Id = t2.Id)
WHEN MATCHED THEN
UPDATE SET t1.Count = Coalesce(t1.Count,0) - Coalesce(t2.Count,0)
Demo for Oracle Case
Coalesce() might be replaced with Nvl() for Oracle DB.

Match all values in IN clause

here is my data:
T1
P.ID UniqueID Value
1 1 Apple
1 2 Orange
1 3 Grapes
2 4 Peach
2 5 Orange
2 6 Banana
T2 (lookup table)
Value
Apple
Orange
Grapes
Peach
Melon
Berry
I need categorize the data into 2 categories:
if all records of each parent id in T1 have a match in T2, then Type1
if any one of the records in T1 for the parent id does not have a
match in T2, then Type2.
Currently, I am using an IN clause to query T2
select ID from T1 where T1.value in(select value from T2)
But I seem to be getting records that do not have a match in T2 as well using thie logic. how do I differentiate these? In my example above, ID 2 should be Type 2, and ID 1 is Type 1.
You could use this query:
select T1.ID,
case count(*) when count(T2.value)
then 'Type1'
else 'Type2'
end as Type
from T1
left join T2 on T1.value = T2.value
group by T1.ID
If you need two queries anyway:
To get type 2:
select unique p_id from t1 where value not in (select value from t2)
Then save (store) the result in a temp table, or make this into a view. Either way, let's say you call it t3.
To get type 1:
select unique p_id from t1 where p_id not in (select p_id from t3)
You could use a temp result that checks for each row separately if the value exists in the lookup table, and then select the max of these checks for each group. Thereby, if at least one row in a group has no reference (i.e. a Type 2 for this row), then the max for the group is Type 2, too:
select id, max(type)
from
(select id, nvl(t2.value,'Type 2', 'Type 1') as type
from t1 left join t2 on t1.value = t2.value)
group by id

Self join for two same rows with one different column

Hi I have table with the following data
A B bid status
10 20 1 SUCCESS_1
10 20 1 SUCCESS_2
10 30 2 SUCCESS_1
10 30 2 SUCCESS_2
Now I want to print or count above rows based on SUCCESS_1 and SUCCESS_2. I created the following query but it does not work it just returns one row by combining two rows.
select * from tbl t1 join tbl t2 on
on (t1.A=t2.A and t1.B=t2.B and
(t1.Status = 'SUCCESS_1' and t2.Status = 'SUCCESS_2')
where t1.bid= 1
I want output as the following for the above query
A B bid status
10 20 1 SUCCESS_1
10 20 1 SUCCESS_2
I am new to SQL please guide. Thanks in advance.
If you need to do the join for some reason (e.g. your database does not let you select everything if you group by 1 column, because it wants everything projected to either be grouped or be an aggregate), you could do the following:
select t1.*
from tbl t1 join tbl t2
on (t1.A=t2.A and t1.B=t2.B and t1.Status = 'SUCCESS_1' and t2.Status = 'SUCCESS_2')
where t1.bid= 1
union all select t2.*
from tbl t1 join tbl t2
on (t1.A=t2.A and t1.B=t2.B and t1.Status = 'SUCCESS_1' and t2.Status = 'SUCCESS_2')
where t1.bid= 1
order by 1,2,3,4
Your original query is pulling back all the data in one row, but this one pulls back the two rows that make that resulting join row separately.
SELECT * FROM `tbl1` WHERE `bid`=1 GROUP BY `status`

Query three non-bisecting sets of data

I am retrieving three different sets of data (or what should be "unique" rows). In total, I expect 3 different unique sets of rows because I have to complete different operations on each set of data. I am, however, retrieving more rows than there are in total in the table, meaning that I must be retrieving duplicate rows somewhere. Here is an example of my three sets of queries:
SELECT DISTINCT t1.*
FROM table1 t1
INNER JOIN table2 t2
ON t2.ID = t1.ID
AND t2.NAME = t1.NAME
AND t2.ADDRESS <> t1.ADDRESS
SELECT DISTINCT t1.*
FROM table1 t1
INNER JOIN table2 t2
ON t2.ID = t1.ID
AND t2.NAME <> t1.NAME
AND t2.ADDRESS <> t1.ADDRESS
SELECT DISTINCT t1.*
FROM table1 t1
INNER JOIN table2 t2
ON t2.ID <> t1.ID
AND t2.NAME = t1.NAME
AND t2.ADDRESS <> t1.ADDRESS
As you can see, I am selecting (in order of queries)
Set of data where the id AND name match
Set of data where the id matches but the name does NOT
Set of data where the id does not match but name DOES
I am retrieving MORE rows than exist in T1 when adding up the number of results returned from all three queries which I don't think is logically possible, plus this means I must be duplicating rows (if it is logically possible) somewhere which prevents me from executing different commands against each set (since a row would have another command executed on it).
Can someone find where I'm going wrong here?
Consider if Name is not unique. If you have the following data:
Table 1 Table 2
ID Name Address ID Name Address
0 Jim Smith 1111 A St 0 Jim Smith 2222 A St
1 Jim Smith 2222 B St 1 Jim Smith 3333 C St
Then Query 1 gives you:
0 Jim Smith 1111 A St
1 Jim Smith 2222 B St
Because rows 1 & 2 in Table 1 match rows 1 & 2, respectively in Table 2.
Query 2 gives you nothing.
Query 3 gives you
0 Jim Smith 1111 A St
1 Jim Smith 2222 B St
Because row 1 in Table 1 matches row 2 in Table 2 and row 2 in Table 1 matches row 1 in Table 2. Thus you get 4 rows out of Table 1 when there are only 2 rows in it.
Are you sure that NAME and ID are unique in both tables?
If not, you could have a situation, for example, where table 1 has this:
NAME: Fred
ID: 1
and table2 has this:
NAME: Fred
ID: 1
NAME: Fred
ID: 2
In this case, the record in table1 will be returned by two of your queries: ID and NAME both match, and NAME matches but ID doesn't.
You might be able to narrow down the problem by intersecting each combination of two queries to find out what the duplicates are, e.g.:
SELECT DISTINCT t1.*
FROM table1 t1
INNER JOIN table2 t2
ON t2.ID = t1.ID
AND t2.NAME = t1.NAME
AND t2.ADDRESS <> t1.ADDRESS
INTERSECT
SELECT DISTINCT t1.*
FROM table1 t1
INNER JOIN table2 t2
ON t2.ID = t1.ID
AND t2.NAME <> t1.NAME
AND t2.ADDRESS <> t1.ADDRESS
Assuming that T2.ID has a unique constraint, it is still quite logically possible for this scenario to occur.If for every record in T1, there are two corresponding records in T2:
Same name, same id, different address
Same name, different id, different address
Then the same record for T1 can come up in the first and third query for example.
It is also possible to simultaneously get the same row in the second and third query.
If T2.ID is not guaranteed to be unique, then you could get the same row from T1 in all three queries.
I think the last query could be the one fetching extra set of rows.
i.e. It is relying on Name matching in both tables (and not on ID)
To find the offending data (and help find your logic hole) I would recommend:
(caution pseudo-code)
Limit the results to just SELECT id FROM ....
UNION the result sets
COUNT(id)
GROUP BY id
HAVING count(id) > 1
This will show the records that match more than one sub-query.