Get number of rows with values less than current row's value - mysql

My mysql table is:
Id | value | count_of_past_lower_values
1 | 120 | 0
2 | 210 | 1
3 |150 | 1
4 |140 | 1
5 |200 | 3
Given id and values, I have to update 3rd col. I made a query using SUM(IF(value<X,1,0)) but not able to identify right expression for X.

Issuing a self-left-join with conditional sum would suffice:
select
a.id,
a.value,
sum(case when a.value > b.value then 1 else 0 end) as count_of_past_lower_values
from yourtable a
left join yourtable b on a.id > b.id
group by a.id, a.value
order by a.id
Result
id | value | count_of_past_lower_values
----+-------+----------------------------
1 | 120 | 0
2 | 210 | 1
3 | 150 | 1
4 | 140 | 1
5 | 200 | 3

Related

MySQL inner join limit 1 row from second table

I have 2 (MySQL) tables , exchange table can have 1--n rows in exchitems, when an exchange record has multiple rows, I would like to display the word "multi", but when there is only 1 row, I would like to display the row's details:
First table (exchange):
xid (PK) | cusid | xdate | xref | xtotal
1 | 1 | 2021-10-01 | 345667 | 500
2 | 1 | 2021-10-01 | 345668 | 200
3 | 1 | 2021-10-02 | 345669 | 450
4 | 1 | 2021-10-03 | 345670 | 1200
And the second table (exchitems):
chid (PK) | xid | cusid | xcur| xsell| xbuy
1 | 1 | 1 | USD | 300 | 0
2 | 1 | 1 | EUR | 0 | 400
3 | 2 | 1 | USD | 200 | 0
4 | 3 | 1 | EUR | 0 | 500
5 | 4 | 1 | EUR | 0 | 800
6 | 4 | 1 | USD | 300 | 0
The exchange table must have at least 1 row in exchtiems table, and this is what I would like to get:
xid | cusid | xdate | xref | xcur | xsell | xbuy | xtotal
1 | 1 | 2021-10-01 | 345667 | multi | 0 | 0 | 500
2 | 1 | 2021-10-01 | 345668 | USD | 200 | 0 | 200
3 | 1 | 2021-10-02 | 345669 | EUR | 0 | 500 | 450
4 | 1 | 2021-10-03 | 345670 | multi | 0 | 0 | 1200
Using the following query, i am able to get the all records, but I would like to limit the exchitems table to one row "any row" when there are multiple rows, the count is used to display the word "multi" when it is > 1:
SELECT a.xid,a.xdate,a.xref,a.xtotal,b.xcur,b.xsell,b.xbuy,
(SELECT COUNT(*) FROM exchitems c WHERE c.xid= a.xid) AS tRec
FROM (exchange a
INNER JOIN exchitems b ON a.xid= b.xid AND a.cusid= b.cusid)
WHERE a.cusid = 1
ORDER BY a.xdate DESC,a.xid DESC
I have tried many different queries but couldn't achieve what I want.
Any help is highly appreciated.
Untested, but this should work.
SELECT
a.xid,
a.cusid,
a.xdate,
a.xref,
-- if distinct currency in the group is > 1 then the word 'multi', else currency.
IF(COUNT(DISTINCT b.xcur) > 1, 'multi', b.xcur) AS `xcur`,
b.xsell,
b.xbuy,
a.xtotal
FROM exchange a
JOIN exchitems b ON a.xid = b.xid AND a.cusid = b.cusid
WHERE a.cusid = 1
GROUP BY xid -- will let you have exchange rows with groups of exchitems 1:n
ORDER BY a.xdate DESC, a.xid DESC
You can modify your current query to the following:
SELECT a.xid, a.cusid, a.xdate,
a.xref,
GROUP_CONCAT(b.xcur),
MIN(b.xsell),
MIN(b.xbuy),
MAX(a.xtotal)
FROM (exchange a
INNER JOIN exchitems b ON a.xid= b.xid AND a.cusid= b.cusid)
WHERE a.cusid = 1
GROUP BY a.xid,a.cusid,a.xdate,a.xref
ORDER BY a.xid;
The result will look like this:
xid
cusid
xdate
xref
GROUP_CONCAT(b.xcur)
MIN(b.xsell)
MIN(b.xbuy)
MAX(a.xtotal)
1
1
2021-10-01
345667
USD,EUR
0
0
500
2
1
2021-10-01
345668
USD
200
0
200
3
1
2021-10-02
345669
EUR
0
500
450
4
1
2021-10-03
345670
EUR,USD
0
0
1200
The part where I use MIN and MAX is according to your expected result. You may want to clarify which value to show there is you have multiple value. If I change that to GROUP_CONCAT:
SELECT a.xid, a.cusid, a.xdate,
a.xref,
GROUP_CONCAT(b.xcur),
GROUP_CONCAT(b.xsell),
GROUP_CONCAT(b.xbuy),
GROUP_CONCAT(a.xtotal)
FROM (exchange a
INNER JOIN exchitems b ON a.xid= b.xid AND a.cusid= b.cusid)
WHERE a.cusid = 1
GROUP BY a.xid,a.cusid,a.xdate,a.xref
ORDER BY a.xid;
Then you'll see a more elaborate result:
xid
cusid
xdate
xref
GROUP_CONCAT(b.xcur)
GROUP_CONCAT(b.xsell)
GROUP_CONCAT(b.xbuy)
GROUP_CONCAT(a.xtotal)
1
1
2021-10-01
345667
USD,EUR
300,0
0,400
500,500
2
1
2021-10-01
345668
USD
200
0
200
3
1
2021-10-02
345669
EUR
0
500
450
4
1
2021-10-03
345670
EUR,USD
0,300
800,0
1200,1200
To make the xcur value show multi, you probably can do something like:
SELECT a.xid, a.cusid, a.xdate,
CASE WHEN COUNT(b.xcur) > 1 THEN 'multi' ELSE MAX(b.xcur) END AS xcur,
MIN(b.xsell),
MIN(b.xbuy),
MAX(a.xtotal)
FROM (exchange a
INNER JOIN exchitems b ON a.xid= b.xid AND a.cusid= b.cusid)
WHERE a.cusid = 1
GROUP BY a.xid,a.cusid,a.xdate,a.xref
ORDER BY a.xid;
Demo fiddle

Check multiple columns for duplicate and list all records

I have a table with columns ID, Content and Day. I am trying to find all rows that have duplicate Content and Day values and display all rows
SELECT ID,Content, `Day`, Count(*)
FROM table
GROUP BY Content,`Day`
HAVING COUNT(*) > 1
The current code will return a list of duplicate Content and 'Day' values for instance:
ID|Content|Day
1 | a | 1
2 | a | 1
3 | a | 1
4 | b | 2
5 | b | 2
6 | c | 3
7 | c | 4
Will result in:
ID|Content|Day|Count
1 | a | 1 | 3
4 | b | 2 | 2
But I want to display all the unique IDs as well;
ID|Content|Day
1 | a | 1
2 | a | 1
3 | a | 1
4 | b | 2
5 | b | 2
Just make a Sub-Query
select *
from table
where `day` in
(
SELECT ID
FROM table
GROUP BY Content,`Day`
HAVING COUNT(*) > 1
) A
Use that query as a subquery to join against the table again:-
SELECT table.ID, table.Content, table.`Day`
FROM table
INNER JOIN
(
SELECT Content, `Day`, Count(*)
FROM table
GROUP BY Content,`Day`
HAVING COUNT(*) > 1
) sub0
ON sub0.Content = table.Content
AND sub0.`Day` = table.`Day`

Joining tables but needs 0 for empty rows

I don't know how to explain the scenario using words. So am writing the examples:
I have a table named tblType:
type_id | type_name
---------------------
1 | abb
2 | cda
3 | edg
4 | hij
5 | klm
And I have another table named tblRequest:
req_id | type_id | user_id | duration
-------------------------------------------
1 | 4 | 1002 | 20
2 | 1 | 1002 | 60
3 | 5 | 1008 | 60
....
So what am trying to do is, fetch the SUM() of duration for each type, for a particular user.
This is what I tried:
SELECT
SUM(r.`duration`) AS `duration`,
t.`type_id`,
t.`type_name`
FROM `tblRequest` AS r
LEFT JOIN `tblType` AS t ON r.`type_id` = t.`type_id`
WHERE r.`user_id` = '1002'
GROUP BY r.`type_id`
It might return something like this:
type_id | type_name | duration
-------------------------------
1 | abb | 60
4 | hij | 20
It works. But the issue is, I want to get 0 as value for other types that doesn't have a row in tblRequest. I mean I want the output to be like this:
type_id | type_name | duration
-------------------------------
1 | abb | 60
2 | cda | 0
3 | edg | 0
4 | hij | 20
5 | klm | 0
I mean it should get the rows of all types, but 0 as value for those type that doesn't have a row in tblRequest
You could perform the aggregation on tblRequest and only then join it, using a left join to handle missing rows and coalesce to convert the nulls to 0s:
SELECT t.type_id, type_name, COALESCE(sum_duration, 0) AS duration
FROM tblType t
LEFT JOIN (SELECT type_id, SUM(duration) AS sum_duration
FROM tblRequest
WHERE user_id = '1002'
GROUP BY type_id) r ON t.type_id = r.type_id
Select a.type_id, isnull(sum(b.duration), 0)
From tblType a Left Outer Join tblRequest b
ON a.type_id = b.type_id and b.user_id = 1002
Group by a.type_id

MySQL subtracting successive rows in same column

My question is similar to this SO post however the difference is that I do not have a sequential ID column.
I have a table in the following form
ID | length
0 | 5
0 | 7
0 | 10
1 | 3
1 | 8
1 | 12
2 | 1
2 | 2
2 | 4
2 | 5
and I want to get the difference between successive rows in the length column grouped by the ID. So it should give
ID | length | difference
0 | 5 | NULL
0 | 7 | 2
0 | 10 | 3
1 | 3 | NULL
1 | 8 | 5
1 | 12 | 4
2 | 1 | NULL
2 | 2 | 1
2 | 4 | 2
2 | 5 | 1
I'm not sure how to go about doing this. I tried giving each ID a separate ID that is sequential, but it turned out to be way to complicated and I could not get it to work. Can someone suggest a better way of doing it?
Assuming (id,length) is UNIQUE...
SELECT x.*
, x.length - MAX(y.length) diff
FROM my_table x
LEFT
JOIN my_table y
ON y.id = x.id
AND y.length < x.length
GROUP
BY x.id
, x.length;
select a.id, a.length, b.length, b.length - a.length as difference
from mytable a, mytable b
where a.id=b.id
and b.length = (select min(length) from mytable where id=a.id and length > a.length)

Count rows with specific value over multiple rows

Its very hard for to set a proper title, because I dont know how I describe my problem.
I have a table like this:
dlID | dl_seID | dlEpisode | dlFlag
___________________________________
1 | 1 | 1 | 0
2 | 1 | 2 | 1
3 | 1 | 3 | 1
4 | 2 | 1 | 1
5 | 2 | 2 | 0
6 | 3 | 1 | 0
What i want is a select query where I get something like this:
dlID | dl_seID | dlEpisode | dlFlag | dlFlagCount
_________________________________________________
1 | 1 | 1 | 0 | 2
2 | 1 | 2 | 1 | 2
3 | 1 | 3 | 1 | 2
4 | 2 | 1 | 1 | 1
5 | 2 | 2 | 0 | 1
6 | 3 | 1 | 0 | 0
dlFlagCount shoud be a counter of dlFlag = 1 where dl_seID = dl_seID.
Second try:
I need a value where I see how many Flags have the value 1 with the same dl_seID.
Is that possible?
I hope you guys know what I want^^
Regards
Try this:
select
a.*,
ifnull(b.ctflags,0)
from
tablea a left join
( select dl_seID, count(dlFlag) ctflags
from tablea
where dlFlag=1
group by dl_seID ) b on (a.dl_seID = b.dl_seID)
The left join is just to get the registry with 0 flags
See the fiddle: http://sqlfiddle.com/#!2/ef9b0/5
EDIT:
As op requested some explanation, here it goes:
What you asked is to count the amount of flags by the dl_seID and to do that you need to do this you separeta your problems, first you get the count for the dl_seID by flags, this is this subquery:
select dl_seID, count(dlFlag) ctflags
from tablea
where dlFlag=1
group by dl_seID
This became a 'separe table' or a new group of data, whatever you wanna call it. Then you have to join this with your original data (from your table) like the query for answer.
The left join part is because maybe there are some data that wont complain with where dlFlag=1 therefore if you want to get then as 0 you have to bring all values from table that exists or not on our created subgroup. And this ifnull(b.ctflags,0) is for theese data data exists on your table but has no flags (for your problem). If you use just b.ctflags it will bring null.
SELECT x.*
, COALESCE(y.flagcount,0) flagcount
FROM my_table x
LEFT
JOIN
( SELECT seID
, COUNT(*) flagcount
FROM my_table
WHERE flag = 1
GROUP
BY seid
) y
ON y.seid = x.seid;