MySQL: Selecting where only one unique Max record exists - mysql

My data is:
bid_id, fkp_id, fkb_id, bid_amount
1 , 13 , 1 , 22000
2 , 13 , 2 , 23000
3 , 13 , 2 , 23000
4, 3 , 1 , 5000
5, 3 , 2 , 6000
If there is a tie (in this case bid_id 2 and bid_id 3) this record should not be selected and only unique max bid_amount value record should be selected.
in this case desired record is bid_id 1 and bid_id 5

Looking to your data if you need all with one unique max amount
and the max amount is related to the amount itself you could use
select bid_id, fkp_id, fkb_id, bid_amount
from table_name
where bid_amount = ( select max(t.bid_amount)
from ( select bid_amount, count(*)
from table_name
group by bid_amount
having count(*) = 1 ) t )
If your unique max amount is for flp_id then you shoulf group by properly for this columns too
select bid_id, fkp_id, fkb_id, bid_amount
from table_name
where ( fkp_id, bid_amount) in ( select t.fkp_id, max(t.bid_amount)
from ( select fkp_id, bid_amount, count(*)
from table_name
group by fkp_id, bid_amount
having count(*) = 1 ) t
group by t.fkp_id )

Related

How to get number of same id's where the item was found in mysql?

Let's say I have table like this:
some_id
date
1
2022-02-01
2
2022-02-02
3
2022-02-03
3
2022-02-04
3
2022-02-05
3
2022-02-06
I want to get the number of rows based on the id where the date was found?
I tried this but it's not working:
SELECT COUNT(id) FROM dates WHERE date = '2022-02-04'
Expected output should be 4 rows since there are 4 same id's where the 2022-02-04 was found.
This should do the job:
SELECT COUNT(*) FROM tbl
WHERE id IN (
SELECT id FROM tbl WHERE `date`='2022-02-04'
)
An exists query should do it:
SELECT id, COUNT(*)
FROM t
WHERE EXISTS (
SELECT 1
FROM t AS x
WHERE x.id = t.id
AND x.date = '2022-02-04'
)
GROUP BY id
Using exists logic we can try:
SELECT COUNT(*)
FROM dates d1
WHERE EXISTS (SELECT 1 FROM dates d2
WHERE d2.some_id = d1.some_id AND
d2.date = '2022-02-04');

SQL query to find new column

I need your help. I have a table named Test_Result with 2 columns as shown below.
ID Source_ID
10 1
20 2
30 2
40 3
50 3
60 3
70 4
I am trying to get output as below,but unable to get logic.
ID Parent_ID Source_ID
10 Null 1
20 Null 2
30 20 2
40 Null 3
50 40 3
60 50 3
70 Null 4
Kindly help me with this scenario. I attached question in picture for as well.
Regards,
Abhi
These solutions (ROW_NUMBER/LAG) will work for MySQL 8.0+ or MariaDB 10.2
You could use ROW_NUMBER() and join to previous row:
CREATE TABLE tab(ID INT ,Source_ID INT);
INSERT INTO tab(id, Source_id)
SELECT 10, 1
UNION ALL SELECT 20 , 2
UNION ALL SELECT 30, 2
UNION ALL SELECT 40 , 3
UNION ALL SELECT 50 , 3
UNION ALL SELECT 60 , 3
UNION ALL SELECT 70 , 4;
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(ORDER BY id) AS rn
FROM tab
)
SELECT c1.ID,
CASE WHEN c1.Source_ID = c2.Source_ID THEN c2.Id END AS Parent_Id,
c1.Source_ID
FROM cte c1
LEFT JOIN cte c2
ON c1.rn = c2.rn+1;
Rextester Demo
EDIT:
Using LAG() windowed function:
SELECT c1.ID,
CASE
WHEN c1.Source_ID = LAG(Source_ID) OVER w THEN LAG(ID) OVER w
END AS Parent_Id,
c1.Source_ID
FROM tab c1
WINDOW w AS (ORDER BY ID)
ORDER BY id;
DBFiddle
EDIT2:
Simulating LAG using variables:
SET #lag_Source_id='';
SET #lag_Id = '';
SELECT ID,
CASE WHEN Source_Id = lag_Source_ID THEN lag_ID END AS Parent_ID
,Source_ID
FROM (
SELECT ID
, Source_ID
, #lag_Source_id AS lag_Source_id
, #lag_Source_id:= Source_ID AS curr_Source_ID
, #lag_Id AS lag_ID
, #lag_Id := ID AS curr_ID
FROM tab
ORDER BY id
) AS sub
RextesterDemo2
if you are using mysql database simply do this,
SELECT ID, (ID + Source_ID) AS Parent_ID, Source_ID FROM tableName LIMIT 10;

MySql query to count the score of each user

The query needs to counts the total points for each user. For each qid the user with lower time gets a point and total point is sum of all the points. Below query just returns the total attempts for each user, need a way to return the points.
Incorrect Query=select user, count(*) from (select * from (select * from xyz order by
time ASC) as temp1 group by temp1.user,temp1.qid) AS temp2 group by user
DB:
CREATE TABLE xyz (
id INT PRIMARY KEY AUTO_INCREMENT,
user VARCHAR(20),
time INT,
qid INT
);
INSERT INTO xyz VALUES ( 1 , 'abc' , 15 , 1);
INSERT INTO xyz VALUES ( 2 , 'abc' , 6 , 1);
INSERT INTO xyz VALUES ( 3 , 'xyz' , 11 , 1);
INSERT INTO xyz VALUES ( 4 , 'abc' , 4 , 1);
INSERT INTO xyz VALUES ( 5 , 'xyz' , 13 , 2);
INSERT INTO xyz VALUES ( 6 , 'abc' , 11 ,2);
INSERT INTO xyz VALUES ( 7 , 'abc' , 9 , 3);
INSERT INTO xyz VALUES ( 8 , 'xyz' , 10 , 3);
INSERT INTO xyz VALUES ( 9 , 'xyz' , 2 , 3);
INSERT INTO xyz VALUES ( 10 , 'xyz' , 2 , 4);
Expected output:
USER Score
abc 2
xyz 2
Output Explanation:
For qid=1, abc has lower time so 1 point to abc
For qid=2, abc has lower time so 1 point to abc
For qid=3, xyz has lower time so 1 point to xyz
For qid=4, xyz has lower time so 1 point to xyz
sqlfiddle Link
You need to find the number of times that a user is "first" for each qid. Here is one method:
select xyz.user, count(*) as score
from xyz join
(select qid, min(time) as mintime
from xyz
group by qid
) q
on xyz.qid = q.qid and xyz.time = q.mintime
group by xyz.user;

How to get first record and last record in a date

I have a table structured in below format :
**ID StationID Status UpdateTime**
1 1 xxxx 2014-09-25 00:01:05
2 1 xxxy 2014-09-25 01:05:18
3 1 xxxz 2014-09-25 09:23:05
4 1 xxxx 2014-09-25 20:01:05
5 1 xxxz 2014-09-25 23:21:34
6 1 xxxz 2014-09-26 00:01:00
7 1 xxxx 2014-09-26 01:45:24
8 1 xxxy 2014-09-26 14:01:43
9 1 xxxx 2014-09-26 22:01:27
For each day, I want to select first record and last record.
Here is result:
**ID StationID Status UpdateTime**
1 1 xxxx 2014-09-25 00:01:05
5 1 xxxy 2014-09-25 23:21:34
6 1 xxxz 2014-09-26 00:01:00
9 1 xxxx 2014-09-26 22:01:27
Thanks all;
Try this:
select *
from YOUR_TABLE yt
join (select min(UpdateTime) minUpdateTime, max(UpdateTime) maxUpdateTime
from YOUR_TABLE
group by DATE_FORMAT(UpdateTime, '%Y-%m-%d')
) aggyt on aggyt.minUpdateTime = yt.UpdateTime or aggyt.maxUpdateTime = yt.UpdateTime
select * from mytable
where UpTime= (select min(UpTime) from mytable group by date_format(UpTime,'%Y-%m-%d'))
or UpTime= (select max(UpTime) from mytable group by date_format(UpTime,'%Y-%m-%d'))
order by id
select * from mytable
where UpTime = (select min(UpTime) from mytable group by date_format(UpTime,'%Y-%m-%d'))
or UpTime = (select max(UpTime) from mytable group by date_format(UpTime,'%Y-%m-%d'))
order by id
May be this will work
SELECT ID,StationId,Status,MAX(UpdateTime) FROM YOUR_TABLE GROUP BY DATE(UpdateTime)
UNION ALL
SELECT ID,StationId,Status,MIN(UpdateTime) FROM YOUR_TABLE GROUP BY DATE(UpdateTime)
If you want to do this by union
SELECT ID, StationId, Status, max(UpdateTime) FROM YOUR_TABLE GROUP BY DATE(UpdateTime)
UNION ALL
SELECT ID, StationId, Status, min(UpdateTime) FROM YOUR_TABLE GROUP BY DATE(UpdateTime)
Just to add, to increase the efiiciency use UNION ALL rather than UNION (or UNION DISTINCT) as UNION requires internal temporary table with index (to skip duplicate rows) while UNION ALL will create table without such index.
If you want to do this by union and subquery
select * from table where id IN ( select max(id) from table GROUP BY DATE(UpdateTime) )
union all
select * from table where id IN ( select min(id) from table GROUP BY DATE(UpdateTime) )
If you want to do it by subquery
SELECT * from table
WHERE id IN ( select max(id) from table GROUP BY DATE(UpdateTime) )
OR id IN ( select min(id) from table GROUP BY DATE(UpdateTime) )

Grouping to find min,max for each group

This would be relatively easy if I only cared about a single min and max for each group, the problem is my requirement is to find the various boundaries. An example data set is as follows:
BoundaryColumn GroupIdentifier
1 A
3 A
4 A
7 A
8 B
9 B
11 B
13 A
14 A
15 A
16 A
What I need from the sql is a result set as follows:
min max groupid
1 7 A
8 11 B
13 16 A
Essentially finding the boundaries for each cluster of the groups.
The data would be stored in either oracle11g or mysql so syntax can be provided for either platform.
A disclaimer: It's a lot easier to query partial results and process something like this with a front-end language. That said...
The following query works for Oracle (which supports analytic queries) but not for MySQL (which does not). There's a SQL Fiddle here.
WITH BoundX AS (
SELECT * FROM (
SELECT
BoundaryColumn,
GroupIdentifier,
LAG(GroupIdentifier) OVER (ORDER BY BoundaryColumn) AS GIDLag,
LEAD(GroupIdentifier) OVER (ORDER BY BoundaryColumn) AS GIDLead
FROM MyTable
ORDER BY BoundaryColumn
)
WHERE GIDLag IS NULL OR GroupIdentifier <> GIDLag
OR GIDLead IS NULL OR GroupIdentifier <> GIDLead
)
SELECT MIN, MAX, GROUPID
FROM (
SELECT
BoundaryColumn AS MIN,
LEAD(BoundaryColumn) OVER (ORDER BY BoundaryColumn) AS MAX,
GroupIdentifier AS GROUPID,
GIDLag,
GIDLead
FROM BoundX
)
WHERE GROUPID = GIDLead
Here's the logic, step by step. You may be able to improve on this, because I get the feeling there's one subquery too many here...
This query pulls the prior and following GroupIdentifier values into each row:
SELECT
BoundaryColumn,
GroupIdentifier,
LAG(GroupIdentifier) OVER (ORDER BY BoundaryColumn) AS GIDLag,
LEAD(GroupIdentifier) OVER (ORDER BY BoundaryColumn) AS GIDLead
FROM MyTable
ORDER BY BoundaryColumn
The result looks like this:
BoundaryColumn GroupIdentifier GIDLag GIDLead
1 A A
3 A A A
4 A A A
7 A A B
8 B A B
9 B B B
11 B B A
13 A B A
14 A A A
15 A A A
16 A A
If you add logic to get rid of all the rows where GIDLag = GIDLead = GroupIdentifier, you'll end up with the boundaries:
WITH BoundX AS (
SELECT * FROM (
SELECT
BoundaryColumn,
GroupIdentifier,
LAG(GroupIdentifier) OVER (ORDER BY BoundaryColumn) AS GIDLag,
LEAD(GroupIdentifier) OVER (ORDER BY BoundaryColumn) AS GIDLead
FROM MyTable
ORDER BY BoundaryColumn
)
WHERE GIDLag IS NULL OR GroupIdentifier <> GIDLag
OR GIDLead IS NULL OR GroupIdentifier <> GIDLead
)
SELECT
BoundaryColumn AS MIN,
LEAD(BoundaryColumn) OVER (ORDER BY BoundaryColumn) AS MAX,
GroupIdentifier AS GROUPID,
GIDLag,
GIDLead
FROM BoundX
With this addition the results are:
MIN MAX GROUPID GIDLAG GIDLEAD
--- --- ------- ------ -------
1 7 A A
7 8 A A B
8 11 B A B
11 13 B B A
13 16 A B A
16 A A
Finally, include only those rows where GroupID = GIDLead. That's the query at the top of this answer. The results are:
MIN MAX GROUPID
--- --- -------
1 7 A
8 11 B
13 16 A
Take a look at this site regarding "runs" of data: http://www.sqlteam.com/article/detecting-runs-or-streaks-in-your-data
Armed with the knowledge provided in that link, you could write a query like this:
SELECT BoundaryColumn,
GroupIdentifier,
(
SELECT COUNT(*)
FROM Table T
WHERE T.GroupIdentifier <> TR.GroupIdentifier
AND T.BoundaryColumn <= TR.BoundaryColumn
) as RunGroup
FROM Table TR
Using this information, you could then group by "RunGroup", and select the GroupIdentifier and min/max BoundaryColumn.
EDIT: I've felt the peer pressure, here's an SQLFiddle with my version of the answer: http://www.sqlfiddle.com/#!8/9a24c/4/0
Another approach(Oracle). Here we simply divide result set returned by the query issued against table t1(your table) into logical groups(grp). Each new group starts when a value of GroupIdentifier changes:
select min(q.BoundaryColumn) as MinB
, max(q.BoundaryColumn) as MaxB
, max(q.GroupIdentifier) as groupid
from ( select s.BoundaryColumn
, s.GroupIdentifier
, sum(grp) over(order by s.BoundaryColumn) as grp
from ( select BoundaryColumn
, GroupIdentifier
, case
when GroupIdentifier <> lag(GroupIdentifier)
over(order by BoundaryColumn)
then 1
end as grp
from t1) s
) q
group by q.grp
Result:
MINB MAXB GROUPID
---------- ---------- -------
1 7 A
8 11 B
13 16 A
SQLfiddle Demo