MySQL group multiple rows based on DISTINCT value - mysql

I need to display the last 2 results from a table (results), the results are comprised of several rows with matching submissionId, The number of rows per submission is unknown, and of course I prefer a single query.
Here is the DB table structure
submissionId input value
1 name jay
1 phone 123-4567
1 email test#gmail.com
2 name mo
2 age 32
3 name abe
3 email abe#gmail.com
4 name jack
4 phone 123-4567
4 email jack#gmail.com
Desierd results:
submissionId input value
3 name abe
3 email abe#gmail.com
4 name jack
4 phone 123-4567
4 email jack#gmail.com
Or even better, if I can combine the rows like this:
3 name abe 3 email abe#gmail.com
4 name jack 4 phone 123-4567 4 email jack#gmail.com

One option here is to use a subquery to identify the most recent and next to most recent submissionId:
SELECT submissionId, input, value
FROM yourTable
WHERE submissionId >= (SELECT MAX(submissionId) FROM yourTable) - 1
ORDER BY submissionId
Demo here:
SQLFiddle
Update:
If your submissionId column were really a date type, and you wanted the most recent two dates in your result set, then the following query will achieve that. Note that the subquery in the WHERE clause, while ugly, is not correlated to the outer query. This means that the MySQL optimizer should be able to figure out that it only needs to run it once.
SELECT submissionDate, input, value
FROM yourTable
WHERE submissionDate >=
(SELECT MAX(CASE WHEN submissionDate = (SELECT MAX(submissionDate) FROM yourTable)
THEN '1000-01-01'
ELSE submissionDate
END) FROM yourTable)
ORDER BY submissionDate
SQLFiddle

You can use limit in subqueries in the from clause, so a typical way to write this is:
SELECT submissionDate, input, value
FROM t join
(select distinct submissionDate
from t
order by submissionDate desc
limit 2
) sd
on t.submissionDate = sd.submissionDate;

This is how the query looks like now, so i can get the results with a LIMIT, RANGE, and id/timestamp (with help of Tim and Gordon):
SELECT *
FROM rmyTable t
JOIN
(SELECT DISTINCT sd.submissionId
FROM myTable sd
WHERE sd.questionId = yourId
ORDER BY sd.submissionId
LIMIT 2
) t2
ON t.submissionId = t2.submissionId
WHERE t.formId = yourId
AND dateTime BETWEEN 0000 AND 1111

Related

Mysql data extraction

I have a table called deals, it has records like this for example
id deal_ref objectname status
1 1234 tom correct
2 1234 tom correct
3 1234 jerry wrong
4 1234 tom correct
I need to identify all latest deals where the status is "correct for example, but the last entry(row 4) must meet the following criteria, where the Max ID is equal to the deal_ref and the status is correct
I tried this
select distinct deal_ref, deal_status
from dealv1 d
where d.deal_ref = max(id)
and d.deal_status = 'Prospect'
and date_created between '2022-11-02 00:00:00' and '2022-11-04 00:00:00'
You use other names in your SQL than in the table (deal_status, date_created).
Nevertheless try do it the following:
SELECT *
FROM dealv1 d
WHERE status = 'correct'
ORDER BY ID DESC
LIMIT 1
i donĀ“t get exactly what you are trying to do with the maxID. You just want the one row where deal_ref=max(id) and status is correct?
Then add
AND deal_ref = (SELECT MAX(id) from dealv1)
after 'correct' from the above statement.
Guys the query below worked, but its displaying multiple deal_refs that has the same deal_ref, for example 2 rows below each other with deal_ref 1234 twice.
SELECT *
FROM dealv1 d
WHERE status = 'correct'
ORDER BY ID DESC
LIMIT 1

SQL Query Sequential Month Logins

I have the following SQL table
username
Month
292
10
123
12
123
1
123
2
123
4
345
6
345
7
I want to query it, to get each username's login streak in Count of sequential Month. meaning the end result I am looking for looks like this :
username
Streak
292
1
123
3
345
2
How can I achieve it ? taking into note the Month 12 --> Month 1 issue;
Appreciate your help;
This would give you the result you want:
select username, count(*)
from (
select
username
, month_1
, coalesce(nullif(lead(month_1)
over (partition by username
order by coalesce(nullif(month_1,12),0))
- coalesce(nullif(month_1,12),0),-1),1) as MonthsTillNext
from login_tab
) Step1
where MonthsTillNext=1
group by username
By calculating the difference from the next row, where the next row is defined as the next month_no in ascending order, treating 12 as 0 (refer to the ambiguity I mentioned in my comment). It then just leaves the rows for consecutive months rows, and counts them.
Beware though, in addition to the anomaly around month:12, there is another case not considered: if the months for the user are 1,2,3 and 6,7,8 this would count as Streak:6; is it what you wanted?
One way would be with a recursive CTE, like
WITH RECURSIVE cte (username, month, cnt) AS
(
SELECT username, month, 1
FROM test
UNION ALL
SELECT test.username, test.month, cte.cnt+1
FROM cte INNER JOIN test
ON cte.username = test.username AND CASE WHEN cte.month = 12 THEN 1 ELSE cte.month + 1 END = test.month
)
SELECT username, MAX(cnt)
FROM cte
GROUP BY username
ORDER BY username
The idea is that the CTE (named cte in my example) recursively joins back to the table on a condition where the user is the same and the month is the next one. So for user 345, you have:
Username
Month
Cnt
345
6
1
345
7
1
345
7
2
The rows with cnt=1 are from the original table (with the extra cnt column hardcoded to 1), the row with cnt=2 is from the recursive part of the query (which found a match and used cnt+1 for its cnt). The query then selects the maximum for each user.
The join uses a CASE statement to handle 12 being followed by 1.
You can see it working with your sample data in this fiddle.
The one shared by #EdmCoff is quite elegant.
Another one without recursive and just using conditional logic -
with data_cte as
(
select username, month_1,
case when (count(month_1) over (partition by username) = 1) then 1
when (lead(month_1) over (partition by username order by username) - month_1) = 1 OR (month_1 - lag(month_1) over (partition by username order by username)) = 1 then 1
when (month_1 = 12 and min (month_1) over (partition by username) =1) then 1
end cnt
from login_tab
)
select username, count(cnt) from data_cte group by username
DB Fiddle here.

MYSQL GROUP BY doesn't return the first row in a ordered select

I'm having a problem with returning some rows in an ordered / grouped query.
I'm trying to get the latest row of type 2 on a table, so I used a ORDER first GROUP later approach.
Example table:
id type field1 date (dd/mm/yyyy)
1 1 texta 01/01/2019
2 1 textb 02/01/2019
3 2 textc 01/01/2019
4 2 textd 02/01/2019
5 2 texte 03/01/2019
If I do
SELECT * FROM cars WHERE type = 2 ORDER BY date DESC
it returns:
id type field1 date
5 2 texte 03/01/2019
4 2 textd 02/01/2019
3 2 textc 01/01/2019
Then if I do
SELECT a.*
FROM ( SELECT * FROM cars WHERE type = 2 ORDER BY date DESC ) a
GROUP BY a.type
I gets:
id type field1 date
3 2 textc 01/01/2019
Does GROUP BY don't get the first row of the group? How can I get the latest row like:
id type field1 date
5 2 texte 03/01/2019
Thank you!!!
1st of all, to the best of my knowledge, your query should work ok. Maybe it's a matter of MySQL versions as suggested in the comments
As pwe your question, the solution should be to arrive to the solution not using GROUP BY. how about ORDER BY + LIMIT
For example:
SELECT * FROM cars WHERE type = 2 ORDER BY date DESC LIMIT 1;
In this case, the order by brings the needed row first, the limit makes only one row to be returned
Another approach, less efficient, is by LEFT JOIN + NULL:
SELECT * FROM cars
LEFT JOIN cars2 ON cars.type = cars2.type AND cars2.date > cars.date
WHERE type = 2
AND cars2.id IS NULL
LIMIT 1;
In that approach, the inequality enforces tye latest row not to be joined to the other table
filtering by that criteria will extract the last row
to get the latest row of each type
you can use NOT EXISTS:
select c.*
from cars c
where not exists (
select 1 from cars
where type = c.type and date > c.date
)
MySQL doesn't guarantee the order of the rows in group by for non-aggregated columns, so the query you wrote will not work consistently and will produce non-deterministic values.
To get the data that you wanted, you can perform the following query:
SELECT *
FROM cars
WHERE date = (SELECT date FROM cars WHERE type = 2 ORDER BY date DESC limit 1)
In your query you are grouping by type but trying to get all other columns which are not part of the aggregation, so it will not work correctly, and in case ONLY_FULL_GROUP_BY SQL mode is enabled, the query will be considered invalid.
Related information for group by can be found in official MySQL documentation

Guidance required for sql query

I have a database with one table as shown below. Here I'm trying to write a query to display the names of medication manufactured by the company that manufactures the most number of medications.
By looking at the table we could say the medication names which belongs to the company id 1 and 2 - because those company manufactures the most medication according to this table, but I'm not sure how to write a query for selecting the same i said before.
ID | COMPANY_ID | MEDICATION_NAME
1 1 ASPIRIN
2 1 GLUCERNA
3 2 SIBUTRAMINE
4 1 IBUPROFEN
5 2 VENOFER
6 2 AVONEN
7 4 ACETAMINOPHEN
8 3 ACETAMINO
9 3 GLIPIZIDE
Please share your suggestions. Thanks!
Several ways to do this. Here's one which first uses a subquery to get the maximum count, then another subquery to get the companies with that count, and finally the outer query to return the results:
select *
from yourtable
where companyid in (
select companyid
from yourtable
group by companyid
having count(1) = (
select count(1) cnt
from yourtable
group by companyid
order by 1 desc
limit 1
)
)
SQL Fiddle Demo
This Query might work. I have not tested but the logic is correct
SELECT MEDICATION_NAME
FROM TABLE where
COMPANY_ID=(SELECT
MAX(counted)
FROM ( SELECT COUNT(*) AS counted FROM TABLE ) AS counts);

Arrange data in specific order

I have a user table that contain 8 records. I want to arrange the data in descending order on the basis of field id (that is a primary key of that table) but except id 3 and 5. So eventually the result should be like
id name
-- ----
3 peter
5 david
8 john
7 stella
6 jim
4 jack
2 nancy
1 scott
Except id 3 and 5 rest of the data should be arranged in descending order and 3 and 5 should come in ascending order.
SELECT * FROM user ORDER BY IF(id=3 OR id=5, id, ~id) ASC
something like this:
order by
case
when id = 3 then 999
when id = 5 then 998
else id
end desc
This assumes that you really don't have more than 8 rows. Otherwise you must change the "magic" numbers that move 3 and 5 to the top.
I think the trick here is to use an enum.
SELECT id, name FROM my_table WHERE id IN (3, 5) ORDER BY ASC
UNION
SELECT id, name FROM my_table WHERE id NOT IN(3, 5) ORDER BY DESC
In MySQL, there is a function called FIELD which *returns zero if a value is not found on the list` eg,
SELECT *
FROM tableName
ORDER BY FIELD(id, 5, 3) DESC, id DESC
SQLFiddle Demo
FIELD