Correlated subquery with row number count - mysql

I have a table as follows and what I want is to use get the initial row with least id of each uid group.
The table is as follows
_id uid type
1 a a
2 b bbb #satisfied
3 b ccc
4 b aaa #satisfied
5 a aaa #satisfied
6 b eee
I can already get the initial row using the following correlated subquery
SELECT *
FROM table
WHERE _id IN (
SELECT MIN(_id)
FROM table
WHERE type IN ('aaa','bbb')
GROUP BY uid
);
However, I want the 4th column shown the count of rows satisfied the condition (type IN ('aaa','bbb')), as cnt shown below:
_id uid type cnt
5 a aaa 1
2 b bbb 2
I think I can count this use several joins and then join the result to my code...But this is ugly...Is there any elegant way to achieve this...

You can try this:
SELECT t1.*, t2.cnt
FROM table t1 INNER JOIN (
SELECT MIN(_id) AS id, COUNT(_id) AS cnt
FROM table
WHERE type IN ('aaa','bbb')
GROUP BY uid
) t2 ON t1._id = t2.id
ORDER BY t1.uid

If you are running MySQL 8.0, you can just use window functions for this:
select _id, uid, type, cnt
from (
select
t.*,
count(*) over(partition by uid) cnt,
row_number() over(partition by uid order by _id) rn
from mytable t
where type in ('aaa', 'bbb')
) t
where rn = 1

You can do this without a subquery. In MySQL 8+, you can use this logic:
SELECT DISTINCT MIN(_id) OVER (PARTITION BY uid) as _id,
uid,
FIRST_VALUE(type) OVER (PARTITION BY uid ORDER BY _id) as type,
COUNT(*) OVER (PARTITION BY uid) as cnt
FROM table
WHERE type IN ('aaa', 'bbb');
Unfortunately, MySQL doesn't have a "first" aggregation function, but there is a trick if you like:
SELECT MIN(_id) as _id, uid,
SUBSTRING_INDEX(GROUP_CONCAT(type ORDER BY _id), ',', 1) as type,
COUNT(*) as cnt
FROM table
WHERE type IN ('aaa', 'bbb')
GROUP BY uid;

Related

Count repeated value from a column for each row with same value in mysql

+------+-------+
| name | value |
+======+=======+
| 5 | 0 |
+------+-------+
| 4 | 0 |
+------+-------+
| 3 | 1 |
+------+-------+
| 4 | 1 |
+------+-------+
| 4 | 1 |
+------+-------+
| 5 | 0 |
+------+-------+
I want to obtain the most repeated value for each name in part.
name 5 have the most repeated value 0
name 4 have the most repeated value 1
name 3 have the most repeated value 1
How can i do that in a single query to mysql ?
Thanks
SOLVED
With the select statement from #nvidot and another posts from SO, i found this is a common problem with this type of query.
Newer versions of MySQL come with ONLY_FULL_GROUP_BY enabled by default, and many of the solutions here will fail in testing with this condition.
So the working formula for me was:
SELECT DISTINCT t1.name, MAX(t1.occ), MAX(t2.value)
FROM (select name, value, count(*) as occ from `table` group by name, value order by occ desc) AS t1
JOIN (select name, adstatus, count(*) as occ from `table` group by name, value order by occ desc) AS t2 ON t2.name = t1.name AND t2.occ = (
SELECT MAX(occ) FROM (select name, value, count(*) as occ from `table` group by name, value order by occ desc) t3 WHERE t3.name = t1.name
)
GROUP BY t1.name;
In Oracle's PL/Sql, there is a specific feature can satisfy your request which is called Window function, but in MySql, there is no such thing untile mysql-8.0
SELECT `column`,
COUNT(`column`) AS `value_occurrence`
FROM `my_table`
GROUP BY `column`
ORDER BY `value_occurrence` DESC
Also please visit this link to more clear.
select name, val
from (select name, val, max(occ)
from (select name, val, count(*) as occ
from `sample` group by name, val
order by occ desc) as groups
group by name) as maximums;
Outer most select serves as cosmetic to display only name and val
order by occ desc serves to obtain the correct val
The following might be sufficient:
select name, val
from (select name, val, count(*) as occ
from `sample`
group by name, val
order by occ desc) as groups
group by name;
[edit]: The following should not trigger error as it does not use non-aggregate column and it does not rely on order by. Multiple name rows might exists if there exists multiples maxs for name/val count.
select name,val
from (select name as maxname, max(occ) as maxocc
from (select name, val, count(*) as occ
from `sample`
group by name, val) as counts2
group by name) as maxs
join (select name, val, count(*) as numocc
from `sample`
group by name, val) as counts1
on name = maxname AND numocc = maxocc;

Select all orders except the max order for each distinct customer

Sorry for the poor formatting but as part of a larger problem, I have created a query that produces this table:
id id2
4 7
4 6
1 3
1 2
1 1
How would I extract the rows that don't have the highest id2 for each id1.
What I want:
id id2
4 6
1 2
1 1
I can only seem to figure out how to get rid of the max id2 overall but not for each distinct id1. Any help on actually differentiating the max id2 for each id1 would be appreciated.
You can try below way -
select a.id, a.id2
from tablename a
where a.id2 <> (select max(a1.id2) from tablename a1 where a.id=a1.id)
If you are using MySQL 8+, then RANK() provides one option:
WITH cte AS (
SELECT id, id2, RANK() OVER (PARTITION BY id ORDER BY id2 DESC) rnk
FROM yourTable
)
SELECT id, id2
FROM cte
WHERE rnk > 1
ORDER BY id DESC, id2 DESC;
Demo
instead of a correlated subquery in the where, you can LEFT JOIN and apply not in...
select id, id2
from yourTable YT
LEFT JOIN
( select id, max( id2 ) highestID2
from YourTable
group by id ) TopPerID
on YT.ID = TopPerID.ID
AND YT.ID2 != TopPerID.highestID2
where TopPerID.id IS NULL
Since you can have id values with only one id2 value, you need to check for that situation as well, which you can do by comparing the MAX(id2) value with the MIN(id2) value in a JOIN:
SELECT t1.*
FROM Table1 t1
JOIN (SELECT id, MAX(id2) AS max_id2, MIN(id2) AS min_id2
FROM Table1
GROUP BY id) t2 ON t2.id = t1.id
AND (t1.id2 < t2.max_id2 OR t2.min_id2 = t2.max_id2)
If we add a row 2, 5 to your sample data this correctly gives the result as
id id2
4 6
1 2
1 1
2 5
Demo on SQLFiddle

How to get two max id from two different rows?

I have table customer table, i need get max ids from two different approvers with same request id
How can i implement this scenairo
Expected output.
id request_id approver_id
2 1 2
4 1 1
Thanks for your time
You can apply GROUP BY on your columns request_id & approver_id and then select MAX(id) to get your expected output as below-
SELECT MAX(id),request_id ,approver_id
FROM customer_approved
GROUP BY request_id,approver_id
One approach uses a join to a subquery to restrict to only the max rows you want:
SELECT ca1.id, ca1.request_id, ca1.approver_id
FROM customer_approved ca1
INNER JOIN
(
SELECT approver_id, MAX(id) AS max_id
FROM customer_approved
GROUP BY approver_id
) ca2
ON ca1.approver_id = ca2.approver_id AND
ca1.id = ca2.max_id;
Demo
If you are using MySQL 8+, then you may also use ROW_NUMBER here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY approver_id ORDER BY id DESC) rn
FROM customer_approved
)
SELECT id, request_id, approver_id
FROM cte
WHERE rn = 1;
Demo

Combining tables while changing id - SQL

I'm trying to create a search function in different tables using UNION and what happened is that the id's are duplicating making the search go wrong. How can I merge different tables into one while no id's are in common?
Here is the example
table1
id name desc
1 henry post
2 albert doth
3 jun cloth
table2
id name desc
1 kin revenge
2 pot eve
The result SHOULD be like this
id name desc
1 henry post
2 albert doth
3 jun cloth
4 kin revenge
5 pot eve
Please help me. Thanks.
In most databases, you would add a new id using the ANSI standard row_number() function:
select row_number() over (order by which, id) as newid, name, description
from (select 1 as which, t1.* from table1 t1 union all
select 2 as which, t2.* from table2 t2
) t;
Note that desc is a really bad name for a column, because it is a SQL keyword and usually a reserved word.
EDIT:
MySQL doesn't support this ANSI standard functionality. Instead, use variables:
select (#rn := #rn + 1) as newid, name, description
from (select 1 as which, t1.* from table1 t1 union all
select 2 as which, t2.* from table2 t2
) t cross join
(select #rn := 0) vars
order by which, id;
I've include the order by so the rows remain in the same order that you seem to want them in -- rows from the first table followed by rows from the second table. If you don't care about the order, just drop the order by.
For SQLite, the calculation is much more painful:
with cte as (
select 1 as which, t1.* from table1 t1 union all
select 2 as which, t2.* from table2 t2
)
select (select count(*)
from cte cte2
where cte2.which < cte.which or (ct2.which = cte.which and cte2.id <= cte.id
) as id,
name, description
from cte;
In MySql, you can simulate the row_number() function of Sql Server and Oracle using a mutating variable hack:
set #rownum := 0;
SELECT #rownum:=#rownum+1 AS` row_number`, `name`, `desc`
FROM
(
SELECT `name`, `desc` FROM table1
UNION
SELECT `name`, `desc` FROM table2
) AS x;
SqlFiddle
It looks like you have to Generate Id's so you can make you Union query as Sub select and generate Id's in Outer Query
MySQL does not have any system function like SQL Server’s row_number () to generate the row number for each row. However, it can be generated using the variable in the SELECT statement
SET #row_number:=0;
SELECT #row_number:=#row_number+1 As Id,
NAME,
desc
FROM (SELECT NAME,desc
FROM table1
UNION ALL
SELECT NAME,desc
FROM table2
UNION ALL
........
........) A
Order by NAME -- Change the column in Order by in which order you want to create New ID's

SQL Server Get Max

I have the following table:
ID Date FirstName Dept
1 1/2/12 James Act
1 2/5/12 Mike IT
2 5/6/12 Joe HR
2 7/6/12 Keith IT
What I need to do that for each ID, I need to get the max date.
I need to show ID, Date, FirstName, Dept for the record for each ID that has the Max Date.
So in this case for ID of 1, I would show 1 2/5/12 Mike IT
How do I do this in SQL Server T-SQL?
I know I need to do group by.
The table name is TblAct
You will use the MAX() function with a GROUP BY
select t1.id, t1.date, t1.fname, t1.dept
from tblAct t1
inner join
(
SELECT Max(Date) maxdate, ID
from TblAct
GROUP BY id
) t2
on t1.id = t2.id
and t1.date = t2.maxdate
See SQL Fiddle with Demo
You can do this with windows/ranking functions:
select ID, Date, FirstName, Dept
from (select t.*,
row_number() over (partition by id order by date desc) as seqnum
from t
) t
where seqnum = 1
This is ordering all the rows for each id by date, in reverse order. It then selects the first of them.
dont use group by :
select * from tblAct t1
where date=(select max(date) from tblAct where t1.id = id)
just enjoy.