MYSQL JOIN sorted join, lost - mysql

I have two tables
tbl1:
id
name
tid
1
some text
1
tbl2:
tid
level
related_id
1
1
4
1
2
5
1
3
6
I want to join tbl1 to tbl2 on tbl1.tid = tbl2.tid, I only want one row joined from tbl2 based on the level for example I want the least level first that is level 1 row joined
joined table
id
name
tid
level
related_id
1
some text
1
1
4
is it possible to achieve this?

Try the following CTE, noting that row_number() function is supported only with MySQL v8.0 and higher.
with cte as
(select
tbl1.id, tbl1.name, tbl2.tid, tbl2.level_, tbl2.related_id
, row_number() over (partition by tbl1.id order by level_) as rn
from tbl1 inner join tbl2
on tbl1.tid=tbl2.tid)
select cte.id, cte.name, cte.tid, cte.level_, cte.related_id
from cte where rn=1
See the result from db-fiddle.
In comments, I suggested to use the min(level) over (partition by tid) function in a where clause, but this will not give the required results unless the level field is unique, where it's not the case as I guess.

Related

select only when different value

I have this column: name and price. I don't really know how or why in mysql database there are few line that are double record exactly from the previous line.
how to select all records but show only one of the records if the record is double with a line in front or behind it?
For example I have this records:
id
name
price
1
book
5
2
lamp
7
3
lamp
7
4
book
5
5
book
5
the result I want is:
id
name
price
1
book
5
2
lamp
7
4
book
5
If you want to exclude rows that match the previous name, there are several ways like the following.
Case 1:
If you use MySQL8, you can use the LAG function.
SELECT t1.id,t1.name,t1.price FROM (
SELECT t2.id,t2.name,t2.price,
LAG(t2.name) OVER(ORDER BY t2.id) prev
FROM mytable t2
) t1
WHERE t1.prev IS NULL OR t1.name<>t1.prev
ORDER BY 1
Case 2:
If the ids are continuous without any steps, you will get the expected result by comparing name and the previous id by JOIN.
SELECT t1.id,t1.name,t1.price FROM mytable t1
LEFT JOIN mytable t2
ON t1.name=t2.name AND
t1.id=t2.id-1
WHERE t1.id=1 OR t2.id IS NOT NULL
ORDER BY 1
Case 3:
If the ids are not continuous, there is a way to get the maximum id that does not exceed the other id.
SELECT t1.id,t1.name,t1.price FROM mytable t1
LEFT JOIN mytable t2
ON t1.name=t2.name AND
t1.id=(SELECT MAX(t3.id) FROM mytable t3 WHERE t3.id<t2.id)
WHERE t1.id=1 OR t2.id IS NOT NULL
ORDER BY 1
DB Fiddle
Select distinct is not an option here as id column is always unique. I guess this will work for you:
select min(id), name, price from table_name group by name, price

SQL select maximum number of duplicates value in a column

Here I have this table:
Copies
nInv | Subject | LoanDate | BookCode |MemberCode|
1 |Storia |15/04/2019 00:00:00 |7844455544| 1 |
2 |Geografia |12/09/2020 00:00:00 |8004554785| 4 |
4 |Francese |17/05/2006 00:00:00 |8004894886| 3 |
5 |Matematica |17/06/2014 00:00:00 |8004575185| 3 |
I'm trying to find the value of the highest number of duplicates in the MemberCode column. So in this case I should get 3 as result, as its value appears two times in the table. Also, MemberCode is PK in another table, so ideally I should select all rows of the second table that match the MemberCode in both tables. For the second part I guess I should write something like SELECT * FROM Table2, Copies WHERE Copies.MemberCode = Table2.MemberCode but I'm missing out almost everything on the first part. Can you guys help me?
Use group by and limit:
select membercode, count(*) as num
from t
group by membercode
order by count(*) desc
limit 1;
SELECT MAX(counted) FROM
(SELECT COUNT(MemberCode) AS counted
FROM table_name GROUP BY MemberCode)
Using analytic functions, we can assign a rank to each member code based on its count. Then, we can figure out what its count is.
WITH cte AS (
SELECT t2.MemberCode, COUNT(*) AS cnt,
RANK() OVER (ORDER BY COUNT(*) DESC, t2.MemberCode) rnk
FROM Table2 t2
INNER JOIN Copies c ON c.MemberCode = t2.MemberCode
GROUP BY t2.MemberCode
)
SELECT cnt
FROM cte
WHERE rnk = 1;
Something like this
with top_dupe_member_cte as (
select top(1) MemberCode, Count(*)
from MemberTable
group by MemberCode
order by 2 desc)
select /* columns from your other table */
from OtherTable ot
join top_dupe_member_cte dmc on ot.MemberCode=dmc.MemberCode;

Strange order by in MySQL: order on field A plus duplicate-status from field B

I need to have a very weird ORDER BY. Let's say I have a table like:
pkey id2
1 8
2 3
3 12
4 8
5 7
6 8
7 3
8 1
I need to SELECT in the following order:
Follow pkey EXCEPT when id2 has duplicates. Then do FIRST the duplicates.
So the ORDER BY should give the following result:
pkey id2
1 8
4 8
6 8
2 3
7 3
3 12
5 7
8 1
I have no clue where to start. GROUP BY? Subqueries? Anyone any ideas? Or should I go php on this? I prefer to do it in MySQL.
After my succinct hint, Roemer came up with this solution:
SELECT a.*
FROM table a
JOIN
(SELECT id2, MIN(pkey) as minId FROM table b GROUP BY id2) b
USING (id2)
ORDER BY minId, pkey, id
It uses an aggregating subquery to find the first pkey for each id2, and uses those pkey values in the outer query to control ordering.
I'd join the query on an aggregate query that counts the number of duplicate id2s each pkey has. Since multiple pkeys can have the same number of associated id2s, I'd throw in the minimal pkey just to make sure that you get all the rows with the same pkey together:
SELECT a.pkey, a.id2
FROM mytable a
JOIN (SELECT id2, COUNT(*) AS cnt, MIN(pkey) AS mpkey
FROM mytable
GROUP BY id2) ON a.id2 = b.id2
ORDER BY cnt DESC, mpkey ASC, a.pkey ASC
Uueerdo gave the answer in a comment. I still want to give him credits, but people get confused, so I post this answer anyway, but I invite Uueerdo to post the answer so I can mark his answer as the proper one.
Anyway, the solution:
SELECT a.*
FROM table a
JOIN
(SELECT id2, MIN(pkey) as minId FROM table b GROUP BY id2) b
USING (id2)
ORDER BY minId, pkey, id

MySQL max value in row

I am facing a problem with MySQL query which is a variant of "Id for row with max value". I am either getting error or incorrect result for all my trials.
Here is the table structure
Row_id
Group_id
Grp_col1
Grp_col2
Field_for_aggregate_func
Another_field_for_row
For all rows with a particular group_id, I want to group by fields Grp_col1, Grp_col2 then get max value of Field_for_aggregate_func and then corresponding value of Another_field_for_row.
Query I have tried is like below
SELECT c.*
FROM mytable as c left outer join mytable as c1
on (
c.group_id=c1.group_id and
c.Grp_col1 = c1.Grp_col1 and
c.Grp_col2 = c1.Grp_col2 and
c.Field_for_aggregate_func > c1.Field_for_aggregate_func
)
where c.group_id=2
Among alternative solutions for this problem I want a high performance solution as this will be used for large set of data.
EDIT: Here is the sample set of row and expected answer
Group_ID Grp_col1 Grp_col2 Field_for_aggregate_func Another_field_for_row
2 -- N 12/31/2015 35
2 -- N 1/31/2016 15 select 15 from group for max value 1/31/2016
2 -- Y 12/31/2015 5
2 -- Y 1/1/2016 15
2 -- Y 1/2/2016 25
2 -- Y 1/3/2016 30 select 30 from group for max value 1/3/2016
You can use a sub-query to find the maximums, then join that with the original table, along the lines of:
select m1.group_id, m1.grp_col1, m1.grp_col2, m1.another_field_for_row, max_value
from mytable m1, (
select group_id, grp_col1, grp_col2, max(field_for_aggregate_func) as max_value
from mytable
group by group_id, grp_col1, grp_col2) as m2
where m1.group_id=m2.group_id
and m1.grp_col1=m2.grp_col1
and m1.grp_col2=m2.grp_col2
and m1.field_for_aggregate_func=m2.max_value;
Watch out for when there is more than one max_value for the given grouping. You'll get multiple rows for that grouping. Fiddle here.
Try this.
See Fiddle demo here
http://sqlfiddle.com/#!9/9a3c26/8
Select t1.* from table1 t1 inner join
(
Select a.group_id,a.grp_col2,
A.Field_for_aggregate_func,
count(*) as rnum from table1 a
Inner join table1 b
On a.group_id=b.group_id
And a.grp_col2=b.grp_col2
And a.Field_for_aggregate_func
<=b.Field_for_aggregate_func
Group by a.group_id,
a.grp_col2,
a.Field_for_aggregate_func) t2
On t1.group_id=t2.group_id
And t1.grp_col2=t2.grp_col2
And t1.Field_for_aggregate_func
=t2.Field_for_aggregate_func
And t2.rnum=1
Here first I am assigning a rownumber in descending order based on date. The selecting all the records for that date.

Adding one extra row to the result of MySQL select query

I have a MySQL table like this
id Name count
1 ABC 1
2 CDF 3
3 FGH 4
using simply select query I get the values as
1 ABC 1
2 CDF 3
3 FGH 4
How I can get the result like this
1 ABC 1
2 CDF 3
3 FGH 4
4 NULL 0
You can see Last row. When Records are finished an extra row in this format
last_id+1, Null ,0 should be added. You can see above. Even I have no such row in my original table. There may be N rows not fixed 3,4
The answer is very simple
select (select max(id) from mytable)+1 as id, NULL as Name, 0 as count union all select id,Name,count from mytable;
This looks a little messy but it should work.
SELECT a.id, b.name, coalesce(b.`count`) as `count`
FROM
(
SELECT 1 as ID
UNION
SELECT 2 as ID
UNION
SELECT 3 as ID
UNION
SELECT 4 as ID
) a LEFT JOIN table1 b
ON a.id = b.id
WHERE a.ID IN (1,2,3,4)
UPDATE 1
You could simply generate a table that have 1 column preferably with name (ID) that has records maybe up 10,000 or more. Then you could simply join it with your table that has the original record. For Example, assuming that you have a table named DummyRecord with 1 column and has 10,000 rows on it
SELECT a.id, b.name, coalesce(b.`count`) as `count`
FROM DummyRecord a LEFT JOIN table1 b
ON a.id = b.id
WHERE a.ID >= 1 AND
a.ID <= 4
that's it. Or if you want to have from 10 to 100, then you could use this condition
...
WHERE a.ID >= 10 AND
a.ID <= 100
To clarify this is how one can append an extra row to the result set
select * from table union select 123 as id,'abc' as name
results
id | name
------------
*** | ***
*** | ***
123 | abc
Simply use mysql ROLLUP.
SELECT * FROM your_table
GROUP BY Name WITH ROLLUP;
select
x.id,
t.name,
ifnull(t.count, 0) as count
from
(SELECT 1 AS id
-- Part of the query below, you will need to generate dynamically,
-- just as you would otherwise need to generate 'in (1,2,3,4)'
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
) x
LEFT JOIN YourTable t
ON t.id = x.id
If the id does not exist in the table you're selecting from, you'll need to LEFT JOIN against a list of every id you want returned - this way, it will return the null values for ones that don't exist and the true values for those that do.
I would suggest creating a numbers table that is a single-columned table filled with numbers:
CREATE TABLE `numbers` (
id int(11) unsigned NOT NULL
);
And then inserting a large amount of numbers, starting at 1 and going up to what you think the highest id you'll ever see plus a thousand or so. Maybe go from 1 to 1000000 to be on the safe side. Regardless, you just need to make sure it's more-than-high enough to cover any possible id you'll run into.
After that, your query can look like:
SELECT n.id, a.*
FROM
`numbers` n
LEFT JOIN table t
ON t.id = n.id
WHERE n.id IN (1,2,3,4);
This solution will allow for a dynamically growing list of ids without the need for a sub-query with a list of unions; though, the other solutions provided will equally work for a small known list too (and could also be dynamically generated).