Add a column to result, which is another SELECT with dependency - mysql

This one is a little bit tricky, at least to me to explain, so please, don't get mad if you don't get the point - it's likely caused by my poor explanation.
I want to get one more column from my main SELECT, which will represent number of rows from another table, suiting id of main record.
So, imagine main table, which I am selecting from. I'll call it simply main.
What I want to select from main, basically is:
SELECT * FROM main ORDER BY c1 ASC LIMIT 5
Plus I need one extra column for each row returned, which says number of rows from side table, matching the id:
SELECT COUNT(*) FROM side WHERE m_id = main_id
Maybe an example will tell you a little bit more
id data1 data2 id m_id ...
main ----|-------|------- side -----|------|-----
1 aa ab 1 1
2 xx yy 2 2
3 az bz 3 1
4 1
5 3
6 2
7 1
8 1
9 2
expected result:
id data1 data2 num
----|-------|-------|------
1 aa ab 5
2 xx yy 3
3 az bz 1

A simple way to add the count is with a correlated subquery:
SELECT m.*,
(select count(*) from side s where s.m_id = m.main_id) as side_cnt
FROM main m
ORDER BY c1 ASC
LIMIT 5;
You can also do this by changing the from clause. However, this method only affects the select part of the query.

You should be able to do it as a subquery:
SELECT m.*, ( SELECT COUNT(*) FROM side WHERE m_id = m.main_id ) as num FROM main m ORDER BY c1 ASC LIMIT 5
This basically runs a special query for each result that counts the number of matching results and displays it in the "num" column.

Related

Detect duplicates within two MySQL columns (unique values scattered across columns)

I have a MySQL table that contains identifiers spread across two columns:
left right
1 2
3 6
4 5
Using SQL how to find out whether the table contains any duplicate identifier?
For instance, the example above is OK but the examples below are not OK:
left right
1 2 <--+
3 6 | Not OK
4 1 <--+
left right
1 2
3 3 <-- Not OK
4 5
left right
1 2
3 6 <--+ Not OK
4 6 <--+
I am legally not allowed to modify anything in this database, so my question is not about enforcing this in the schema or via triggers or stored procedures I can not modify the database's schema, but rather how to check using just SELECT-type operation(s).
If it was a single column I would write SELECT left FROM MyTable GROUP BY left HAVING COUNT(*) > 1; but for two columns I am not sure how to proceed...
Use UNION ALL to get all values, left and right, in one column:
select value
from
(
select left as value from mytable
union all
select right as value from mytable
) all_values
group by value
having count(*) > 1;
if any any values of left exist in right then it is duplicate so you use sub-query and aggregate function
select case when count(*)>0 then 'deplicate' else 'not' end as result from your_table
where left in (select distinct right from your_table t2)
http://sqlfiddle.com/#!9/ce69b8/2

Explaning results out of JOIN clause

I have mycash table in MySQL with the following data :
Test Case 1 :
When I run the following query,
SELECT t.id, SUM(prev.cash) AS cash_sum FROM mycash t JOIN mycash prev ON (t.id > prev.id)
I get :
id | cash_sum
2 | 1303.00
Summing up the cash values from all the rows equals to 1302 and NOT 1303.
I change the comparison operator in the ON condition and get the following results:
Test Case 2 :
For ON (t.id < prev.id) , result is:
id | cash_sum
1 | 2603.00
Test Case 3 :
For ON (t.id >= prev.id) , result is:
id | cash_sum
1 | 2605.00
Test Case 4 :
For ON (t.id <= prev.id) , result is:
id | cash_sum
1 | 3905.00
What is the calculation behind each of the results? Step by step explanation would clarify them most.
The results are correct.
200 + 200 + 200 + 301 + 301 + 101 = 1303
Yes, I got the answer my self. It is quite simple.
For any comparison operator in the ON condition, we have to compare each row in table t with all other rows in table prev. The query in any case will output the sum total of all the cash values from all the rows (total number of rows returned may NOT be 4 at most).
Let me explain the test case 1:
SELECT t.id, SUM(prev.cash) AS cash_sum FROM mycash t JOIN mycash prev ON (t.id > prev.id)
We have to select those rows from table prev whose id numbers are less then a particular id in table t.
From the first table, i.e. table t, when the id number is 1, we get no corresponding rows from second table, i.e. table prev. When the id is 2 from 1st table, we have the id 1 (cash =200) from 2nd table. If the id is 3 from 1st table, we have 1 and 2 (cash =200 and 301 respectively) from 2nd table. When the id is 4 from first table, we have ids 1,2,3 (cash = 200,301,101 respectively). So after adding all the cash values, cash_sum becomes 200+200+301+200+301+101 = 1303.
Other test cases can be explained in a similar way.

MySQL calculate only the row-differences for rows in the same group

I have the following table (call it trans):
issue_id: state_one: state_two: timer:
1 A B 1
1 B C 3
2 A B 2
2 B C 4
2 C D 7
I'd like the get the difference in 'timer' between consecutive rows, but only those with the same issue_id.
Expected result:
issue_id: state_one: state_two: timer: time_diff:
1 B C 3 2
2 B C 4 2
2 C D 7 3
When taking the time difference between two rows, I'd like the result displayed next to the later row.
If we only had one, time-ordered issue in the table, the following code works fine:
select
X.issue_id,
X.timer as X_timer,
Y.timer as Y_timer,
(X.timer - Y.timer) as time_diff
from trans X
cross join trans Y
where Y.timer in (
select
max(Z.timer)
from trans Z
where Z.timer < X.timer);
I want to generalize this approach to handle MANY issues with time-ordered state changes.
My idea was to add the following condition, but it only works if consecutive events belong to the same issue (not the case in the real world):
... where Z.timer < X.timer)
and X.issue_id = Y.issueid;
Question: In MySQL, can I do this iteratively (i.e. calculate differences for issue_id=1, then for issue_id=2, and so on)? A function or subquery?
Other strategies? Constraint: I have read-only privileges. I truly appreciate the help!
EDIT: I added expected output, added a row to my example table, and clarified.
select
issue_id, (MAX(timer)-MIN(timer)) as diff from trans
group by issue_id
Assuming timer or (issue_id,timer) is PRIMARY...
SELECT a.*, a.timer-MAX(b.timer)
FROM trans a
JOIN trans b
ON b.issue_id = a.issue_id
AND b.timer < a.timer
GROUP
BY a.issue_id
, a.timer;
Select * from #Temp
Select T1.Issuerid,T1.stateone,T1.statetwo,MAX(T1.timer)-MIN(T.timer) as Time_Diff from #Temp T1
left join #Temp T2 on
T1.issuerid=T2.IssuerId
group by T1.Issuerid,T1.stateone,T1.statetwo
Please Give me Reply

common values b/w fields

This table lists user and item id's
user_id item_id
1 1
1 2
1 3
2 1
2 3
3 1
3 4
3 3
How can I run a query on this table to list all the items that are common between given users.
My guess is, this will need a self join, but I'm not sure.
i am trying this quering but it's returning an error
SELECT *
FROM recs 1
JOIN recs 2 ON 2.user_id='2' AND 2.item_id=1.item_id
WHERE 1.user_id='1'
Try using alias names that start in a letter:
SELECT *
FROM recs r1
JOIN recs r2 ON r2.user_id='2' AND r2.item_id=r1.item_id
WHERE r1.user_id='1'
This returns
user_id item_id
------- -------
1 1
1 3
for your data. Demo on sqlfiddle.
Note: I kept single quotes in the query, because I assume that both IDs in your table are of character type. If that is not the case, remove single quotes around user ID values '1' and '2'.
I want it for n number of users ... a I want the query to return all item_id's that are common among the users
SELECT DISTINCT(r1.item_id)
FROM recs r1
WHERE EXISTS (
SELECT *
FROM recs r2
WHERE r2.item_id=r1.item_id
AND r1.user_id <> r2.user_id
)
Demo #2.

Deleting cross referenced data

I have the following MySQL table:
id rid
----- ------
1 2
2 1
2 3
3 2
1 3
3 1
I want to change this so only one row per relation exists.
e.g:
id rid
----- ------
1 2
2 3
1 3
If you always have pairs (as in your example):
delete from table
where id > rid;
This keeps the record where id is smaller.
If there is the possibility that no all pairs exist, then:
delete t
from table t left outer join
(select least(id, rid) as lid, greatest(id, rid) as gid, count(*) as cnt
from table t2
group by least(id, rid), greatest(id, rid)
) t2
on least(t.id, t.rid) = t2.lid and greatest(t.id, t.rid) = gid
where id < rid or t2.cnt = 1;
EDIT (explanation):
How does the second query work? Let me be honest, what I want to write is this:
delete t from table t
where id < rid or
(id > rid and
not exists (select 1 from table t2 where t2.id = t.rid and t2.rid = t.id
);
That is, I want to keep all records where id < rid. But then, I also want to keep all singleton records where rid > id. I don't think MySQL allows the syntax with the where clause.
Instead, the query in the answer counts the number of times that a pair exists, by looking at the smallest value and the largest value. For the data in the question, the result of the subquery is:
id rid cnt
1 2 2
2 3 2
1 3 2
So, all of these would use the id < rid to select the row. If you had one more row, say 4, 1. It would look like:
lid gid cnt
1 2 2
2 3 2
1 3 2
1 4 1
In this case, the first three would take the row with id < rid. But the new row would also be selected because the cnt is 1.
If you had duplicates in the table and a primary key, there would be a slight variation on the query that would do the same thing.