Get max and min from fields - php

I have two tables:
First:
id | title
1 | aaa
2 | bbb
3 | ccc
Second:
id | first_id | one | two | three | four
1 | 1 | 3 | 1 | 4 | 6
2 | 2 | 4 | 4 | 1 | 2
3 | 3 | 1 | 2 | 3 | 4
and i would like show:
id | title | min | max
1 | aaa | 1 | 6
2 | bbb | 1 | 4
3 | ccc | 1 | 4
Is this possible with SQL? How? :)

Normalize your database. With your current setup it's not impossible but definitely not recommended.
-edit-
If you must, you can use LEAST() and GREATEST()
-edit2-
SELECT
a.id,
a.title,
LEAST(b.one,b.two,b.three,b.four) min,
GREATEST(b.one,b.two,b.three,b.four) max
FROM first a
INNER JOIN second b ON a.id=b.first_id

Read Tom's answer, this would be the best to do.
Anyway, and shame on me :
SELECT f.id, f.title
MIN(LEAST(s.one, s.two, s.three, s.four)) as min,
MAX(GREATEST(s.one, s.two, s.three, s.four)) as max
FROM First f
INNER JOIN Second s on f.id = s.first_id
GROUP BY f.id, f.title
you can remove MIN and MAX (and Group by) if Second can't have many rows with same first_id.

select first_id,F.title ,MIN(num) [min],MAX(num) [max] from (
select first_id,[one] [num]from [Second] union all
select first_id,[two] [num]from [Second] union all
select first_id,[three] [num]from [Second] union all
select first_id,[four] [num] from [Second] )[Second]
join [First] F
on Id=first_id
group by first_id,F.title

You can do that using UNION. try this one out:
SELECT a.id, a.title, MIN(b.c) `Min`, MAX(b.c) `Max`
FROM First a INNER JOIN
(
SELECT first_id, `one` c FROM `Second`
UNION
SELECT first_id, `two` c FROM `Second`
UNION
SELECT first_id, `three` c FROM `Second`
UNION
SELECT first_id, `four` c FROM `Second`
) b on a.id = b.First_ID
GROUP BY a.id
SEE DEMO HERE

Related

Get Rank by votes and number of same rank PHP MySQL

i am trying to get rank and number of same rank by votes but unfortunately no success.
Here my table structure:
| ID| user_id | votes |
| --| ------- | ----- |
| 1 | D10 | 15 |
| 2 | D5 | 9 |
| 3 | D20 | 9 |
| 4 | D23 | 7 |
| 5 | D35 | 3 |
| 6 | D65 | 2 |
I need the rank of user according to votes, referring to above table i need the rank as:
| user_id | Rank|
| ------- | ----|
| D10 | 1 |
| D5 | 2 |
| D20 | 2 |
| D23 | 3 |
| D35 | 4 |
| D65 | 5 |
and also i need the number of rank, referring to above ranks i need:
Rank 1 = 1
Rank 2 = 2
Rank 3 = 1
Rank 4 = 1
rank 5 = 1
i tried to get rank :
SELECT user_id, votes, FIND_IN_SET( votes, (
SELECT GROUP_CONCAT( DISTINCT votes
ORDER BY votes DESC ) FROM table)
) AS rank
FROM votes
the above query i tried referring to this answer to get the ranks but i am getting error:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use
near '( votes , (SELECT GROUP_CONCAT( DISTINCT votes ORDER BY votes DESC
)
i need the desired result using PHP and MySQL.
On MySQL 8+ you could use windows function dense_rank and count over
with votes_rank as (
select *,
dense_rank() over (order by votes desc) as rnk
from votes
) , count_rank as
( select votes_rank.*,
count(*)over (partition by rnk) as cnt
from votes_rank
) select id,
user_id,
votes,
rnk as votes_rank,
cnt as count_rank
from count_rank;
https://dbfiddle.uk/o1DiPyDz
Consider the following data,
CREATE TABLE votes (
id int,
user_id varchar(10),
votes int );
insert into votes values (1,'D10',15), (2,'D5 ',9), (3,'D20',9), (4,'D23',7), (7,'D50',7), (5,'D35',3), (6,'D65',2);
Result:
id user_id votes votes_rank count_rank
1 D10 15 1 1
2 D5 9 2 2
3 D20 9 2 2
4 D23 7 3 2
7 D50 7 3 2
5 D35 3 4 1
6 D65 2 5 1
Edit,
On MySQL version <8
select tbl.id,tbl.user_id,tbl.votes,tbl.rnk,votes_count
from (SELECT a.id,
a.user_id,
a.votes,
count(b.votes)+1 as rnk
FROM votes a
left join votes b on a.votes<b.votes
group by a.id,a.user_id,a.votes
order by a.votes desc
) as tbl
inner join (select rnk,count(rnk) as votes_count
from ( SELECT a.id,
a.user_id,
a.votes,
count(b.votes)+1 as rnk
FROM votes a
left join votes b on a.votes<b.votes
group by a.id,a.user_id,a.votes
order by a.votes desc
) a2
group by rnk
) as tbl1 on tbl1.rnk = tbl.rnk;
https://dbfiddle.uk/XlsBjrZO

Stuck in building MySQL query

Given an example of table:
id | item_id | user_id | bid_price
----------------------------------
The task is to select rows with minimum bid_price for each item_id in the provided set.
For example: item_id = [1, 2, 3] - so I need to select up to three (3) rows, having a minimum bid_price.
Example of data:
id | item_id | user_id | bid_price
----------------------------------
1 | 1 | 11 | 1
2 | 1 | 12 | 2
3 | 1 | 13 | 3
4 | 1 | 14 | 1
5 | 1 | 15 | 4
6 | 2 | 16 | 2
7 | 2 | 17 | 1
8 | 3 | 18 | 2
9 | 3 | 19 | 3
10 | 3 | 18 | 2
Expected result:
id | item_id | user_id | bid_price
----------------------------------
1 | 1 | 11 | 1
7 | 2 | 17 | 1
8 | 3 | 18 | 2
Actually, I'm using Symfony/Docine DQL, but it will be enough with a plain SQL example.
For the all the columns in the rows you could use a inner join on subselect for min bid price
select m.id, m.item_id, m.user_id, m.bid_price
from my_table m
inner join (
select item_id, min(id) min_id, min(bid_price) min_price
from my_table
where item_id IN (1,2,3)
group by item_id
) t on t.item_id = m.item_id
and t.min_price= m.bid_price
and t.min_id = m.id
or .. if you have some float data type you could use a acst for unsigned
select m.id, m.item_id, m.user_id, cast(m.bid_price as UNSIGNED)
from my_table m
inner join (
select item_id, min(id) min_id, min(bid_price) min_price
from my_table
where item_id IN (1,2,3)
group by item_id
) t on t.item_id = m.item_id
and t.min_price= m.bid_price
and t.min_id = m.id
You can use MIN() with GROUP BY in the query:
SELECT id, item_id, MIN(bid_price) AS min_bid, user_id
FROM your_tbl
GROUP BY item_id
HAVING item_id in(1, 2, 3);
Use this query:
SELECT id, item_id, user_id, min(bid_price) as bid_price
FROM YOUR_TABLE_NAME
GROUP BY item_id;

mysql count how many times the same value appears across multiple colums

during a group project we recent sent out a survey regarding the site we're building. I've put the data into a mysql database and i'm trying to figure out how to count how many times certain scores was given in each category
the table looks like this
+-----------------+--------------+-------------------+
| Design | Ease of use | Responsiveness |
+-----------------+--------------+-------------------+
| 5 | 5 | 5
| 4 | 4 | 4
| 3 | 3 | 3
| 2 | 2 | 2
| 1 | 1 | 1
| 5 | 4 | 2
| 5 | 4 | 4
| 3 | 3 | 3
| 1 | 2 | 2
| 1 | 2 | 2
I've found a query that works for one colum
SELECT Design, COUNT(*) AS num FROM table GROUP BY Design
I would then get
Design | num
-------------
5 | 3
4 | 1
3 | 2
2 | 1
1 | 3
If i were to try
SELECT Design, COUNT(*) AS num1, Ease of use, COUNT(*) as num2 FROM table
GROUP BY Design, Ease of use
The table gets totally messed up.
What I want is to get
Design | num1 | Ease of use | num2 | Responsiveness | num3
------------- --------------------------------------------------
5 | 3 | 5 | 1 | 5 | 1
4 | 1 | 4 | 3 | 4 | 2
3 | 2 | 3 | 2 | 3 | 2
2 | 1 | 2 | 3 | 2 | 4
1 | 3 | 1 | 1 | 1 | 1
Any help would be greatly appreciated
You can unpivot the values and then aggregate. In MySQL, that typically uses union all:
select val, count(*)
from ((select design as val from table) union all
(select ease_of_use from table) union all
(select responsiveness from table
) der
group by val
order by val desc;
For what you want to get, you can do:
select val, sum(design) as design, sum(ease_of_use) as ease_of_use,
sum(responsiveness) as responsiveness
from ((select design as val, 1 as design, 0 as ease_of_use, 0 as responsiveness from table) union all
(select ease_of_use, 0, 1, 0 from table) union all
(select responsiveness, 0, 0, 1 from table
) der
group by val
order by val desc;
I see no reason to repeat the value three times.
Use a synthesized table with the different values, and join this with subqueries that get the counts of each score.
SELECT nums.num AS Design, t1.count AS num1,
nums.num AS `Ease of Use`, t2.count AS num2,
nums.num AS Responsiveness, t3.count AS num3
FROM (SELECT 1 AS num UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) AS nums
LEFT JOIN (
SELECT Design, COUNT(*) AS count
FROM yourTable
GROUP BY Design) AS t1 ON t1.Design = nums.num
LEFT JOIN (
SELECT `Ease of Use`, COUNT(*) AS count
FROM yourTable
GROUP BY `Ease of Use`) AS t2 ON t2.`Ease of Use` = nums.num
LEFT JOIN (
SELECT Responsiveness, COUNT(*) AS count
FROM yourTable
GROUP BY Responsiveness) AS t3 ON t3.Responsiveness = nums.num
DEMO
Here are three ways:
select s.score,
(select count(*) from tbl where `Design` = s.score) as `Design`,
(select count(*) from tbl where `Ease of use` = s.score) as `Ease of use`,
(select count(*) from tbl where `Responsiveness` = s.score) as `Responsiveness`
from (
select Design as score from tbl
union select `Ease of use` from tbl
union select Responsiveness from tbl
) s
order by score desc
http://sqlfiddle.com/#!9/002303/2
select s.score,
(select count(*) from tbl where `Design` = s.score) as `Design`,
(select count(*) from tbl where `Ease of use` = s.score) as `Ease of use`,
(select count(*) from tbl where `Responsiveness` = s.score) as `Responsiveness`
from (select 1 as score union select 2 union select 3 union select 4 union select 5) s
order by score desc
http://sqlfiddle.com/#!9/002303/4
select s.score,
sum(`Design` = score) as `Design`,
sum(`Ease of use` = score) as `Ease of use`,
sum(`Responsiveness` = score) as `Responsiveness`
from (select 1 as score union select 2 union select 3 union select 4 union select 5) s
cross join tbl t
group by s.score
order by s.score desc
http://sqlfiddle.com/#!9/002303/5
They all return the same result:
| score | Design | Ease of use | Responsiveness |
|-------|--------|-------------|----------------|
| 5 | 3 | 1 | 1 |
| 4 | 1 | 3 | 2 |
| 3 | 2 | 2 | 2 |
| 2 | 1 | 3 | 4 |
| 1 | 3 | 1 | 1 |
As #futureweb wrote in the comment, I don't see a reason to repeat the score three times. Though you can if you want using aliases.
If you have millions of rows ;-) and no indexes you would want to get the result with only one table scan. This is possible with:
select
sum(`Design` = 1) as d1,
sum(`Design` = 2) as d2,
sum(`Design` = 3) as d3,
sum(`Design` = 4) as d4,
sum(`Design` = 5) as d5,
sum(`Ease of use` = 1) as e1,
sum(`Ease of use` = 2) as e2,
sum(`Ease of use` = 3) as e3,
sum(`Ease of use` = 4) as e4,
sum(`Ease of use` = 5) as e5,
sum(`Responsiveness` = 1) as r1,
sum(`Responsiveness` = 2) as r2,
sum(`Responsiveness` = 3) as r3,
sum(`Responsiveness` = 4) as r4,
sum(`Responsiveness` = 5) as r5
from tbl
This will return the data you need, but not in the form you'd like:
| d1 | d2 | d3 | d4 | d5 | e1 | e2 | e3 | e4 | e5 | r1 | r2 | r3 | r4 | r5 |
|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|
| 3 | 1 | 2 | 1 | 3 | 1 | 3 | 2 | 3 | 1 | 1 | 4 | 2 | 2 | 1 |
So you would need to post process it.

mysql - split columns to rows dynamically

I have the table training, I want to split Training_name Column values to multiple Rows:
SLNO Category Training_name
1 A 1,5,9,15,12,16
2 B 2,6,10,17
3 C 1,3,7,19,14,18
I used below Query but using this Query i can only split into two rows...
SELECT training.SLNO,training.CATEGORY, SubString_Index(training.TRAINING_NAME, ',', 1) AS TRAINING_NAME FROM training UNION ALL SELECT training.SLNO,training.CATEGORY, SubString_Index(training.TRAINING_NAME, ',', -1) FROM training
i am trying to get the table as given below,Please help me out
SLNO Category Training_name
1 A 1
1 A 5
1 A 9
1 A 15
1 A 12
1 A 16
2 B 2
2 B 6
2 B 10
2 B 17
3 C 1
3 C 3
3 C 7
3 C 19
3 C 14
3 C 18
DROP TABLE IF EXISTS my_bad_table;
DROP TABLE IF EXISTS my_good_table;
CREATE TABLE my_bad_table
(SLNO INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,Category CHAR(1) NOT NULL
,Training_name VARCHAR(200) NOT NULL
);
INSERT INTO my_bad_table VALUES
(1,'A','1,5,9,15,12,16'),
(2,'B','2,6,10,17'),
(3,'C','1,3,7,19,14,18');
CREATE TABLE my_good_table AS
SELECT DISTINCT x.SLNO
, x.Category
, CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(x.training_name,',',y.i+1),',',-1) AS UNSIGNED) training_name
FROM my_bad_table x
, (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5
UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 8 UNION SELECT 9) y
ORDER
BY slno
, category
, training_name;
SELECT * FROM my_good_table;
+------+----------+---------------+
| SLNO | Category | training_name |
+------+----------+---------------+
| 1 | A | 1 |
| 1 | A | 5 |
| 1 | A | 9 |
| 1 | A | 12 |
| 1 | A | 15 |
| 1 | A | 16 |
| 2 | B | 2 |
| 2 | B | 6 |
| 2 | B | 10 |
| 2 | B | 17 |
| 3 | C | 1 |
| 3 | C | 3 |
| 3 | C | 7 |
| 3 | C | 14 |
| 3 | C | 18 |
| 3 | C | 19 |
+------+----------+---------------+
If 1s are always 'A', etc, then a further step towards normalization is required to remove that redundancy.
Here is one method:
select slno, category, substring_index(training_name, ',', 1) + 0 as training_id
from t
union all
select slno, category, substring_index(substring_index(training_name, ',', 2), ',', -1) + 0 as training_id
from t
where training_name like '%,%'
union all
select slno, category, substring_index(substring_index(training_name, ',', 3), ',', -1) + 0 as training_id
from t
where training_name like concat('%', repeat(',%', 2))
union all
select slno, category, substring_index(substring_index(training_name, ',', 4), ',', -1) + 0 as training_id
from t
where training_name like concat('%', repeat(',%', 3))
union all
. . .
Repeat for as often as you need. Store the results in a new table. Fix foreign key references and other aspects of the data. Drop the original table (well, archive it) and never use that structure again.
It works to me..
SELECT DISTINCT x.PARENT_SLNO, x.TRAINING_CATEGORY, CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(x.TRAINING_NAME,',',y.i+1),',',-1) AS UNSIGNED) TRAINING_NAME FROM assessment_training x, (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 8 UNION SELECT 9) y where PARENT_SLNO = 3 and TRAINING_CATEGORY='technical' ORDER BY PARENT_SLNO,TRAINING_CATEGORY,TRAINING_NAME

Skip 0 and NULL in SQL

My previous problem and solution:
Get max and min from fields
This working OK, but i would like skip 0 and NULL in this examples.
For example:
First:
id | title
1 | aaa
2 | bbb
3 | ccc
Second:
id | first_id | one | two | three | four
1 | 1 | 3 | 0 | 4 | 6
2 | 2 | 4 | 4 | 1 | 2
3 | 3 | 1 | NULL | 3 | 4
this should show me:
id | title | min | max
1 | aaa | 3 | 6
2 | bbb | 1 | 4
3 | ccc | 1 | 4
and not:
id | title | min | max
1 | aaa | 0 | 6
2 | bbb | 1 | 4
3 | ccc | 0 | 4
In which example from my previous question is the best way to implement skip 0 and NULL?
Pop these into your clause
SELECT
f.id,
f.title
MIN(LEAST(greatest(coalesce(s.one,0),1), greatest(coalesce(s.two,0),1), greatest(coalesce(s.three,0),1), greatest(coalesce(s.four,0),1))) as min,
MAX(GREATEST(greatest(coalesce(s.one,0),1), greatest(coalesce(s.two,0),1), greatest(coalesce(s.three,0),1), greatest(coalesce(s.four,0),1))) as max
FROM
First f
INNER JOIN Second s
on f.id = s.first_id
GROUP BY
f.id,
f.title
You can use coalesce(fieldName, 1) to turn a null into a 1.
Again, as said in your previous question, this is HORRIBLE use of a query to force an answer. You should be changing the layout of the database.
Edit: I have nutted out the data you want, but before you look at it, be aware that if one of my colleagues wrote a script like this, he would be sacked on the spot. This is HIDEOUS and should NOT BE USED.
select
f.id,
f.title,
(select min(z.myVal) from
(
select
b.id,
b.first_id,
b.one as myVal
from
second b
where
b.one is not null
and b.one > 0
union
select
b.id,
b.first_id,
b.two as myVal
from
second b
where
b.two is not null
and b.two > 0
union
select
b.id,
b.first_id,
b.three as myVal
from
second b
where
b.three is not null
and b.three > 0
union
select
b.id,
b.first_id,
b.four as myVal
from
second b
where
b.four is not null
and b.four > 0
) z
where
f.id=z.first_id) as miniVal,
greatest(
coalesce(s.one,0),
coalesce(s.two,0),
coalesce(s.three,0),
coalesce(s.four,0)
) as maxiVal
from
first f,
second s
where
f.id=s.first_id
output Data
+------+-------+---------+---------+
| id | title | miniVal | maxiVal |
+------+-------+---------+---------+
| 1 | aaaa | 3 | 6 |
| 2 | bbbb | 1 | 4 |
| 3 | cccc | 1 | 4 |
+------+-------+---------+---------+
3 rows in set (0.00 sec)
Running this query made me throw up a little in my mouth. That's how wrong it is to write SQL like this.
While seemingly clunky, this solution should work:
SELECT
a.id, a.title, MIN(b.num) AS min, MAX(b.num) AS max
FROM
first a
LEFT JOIN
(
SELECT first_id, one AS num FROM second UNION ALL
SELECT first_id, two FROM second UNION ALL
SELECT first_id, three FROM second UNION ALL
SELECT first_id, four FROM second
) b ON
a.id = b.first_id AND
b.num IS NOT NULL AND
b.num > 0
GROUP BY
a.id, a.title
What this does is it actually gets each number column into its own row, but only the numbers that are not null and > 0. Before the GROUP BY, the result of the LEFT JOIN would look something like:
id | title | num
---------------------
1 | aaa | 3
1 | aaa | 4
1 | aaa | 6
2 | bbb | 1
2 | bbb | 2
2 | bbb | 4
2 | bbb | 4
3 | ccc | 1
3 | ccc | 3
3 | ccc | 4
Then by the groupings of each first (GROUP BY a.id, a.title), we can use the MIN() and MAX() aggregate functions on the num column to extract minimum and maximum values per first group:
id | title | min | max
----------------------------
1 | aaa | 3 | 6
2 | bbb | 1 | 4
3 | ccc | 1 | 4
In the case that a first_id had all four columns having NULL's or 0's, the min and max values would show up as NULL due to using a LEFT JOIN instead of an INNER JOIN as I believe this is would be a better behavior for your situation:
id | title | min | max
----------------------------
4 | ddd | NULL | NULL
USE:
WHERE COLUMN IS NOT NULL AND COLUMN <> 0;
I think you just need to nest the LEAST expressions:
LEAST(
NULLIF(one,0),
LEAST(
NULLIF(two,0),
LEAST(
NULLIF(three,0),
LEAST(
NULLIF(four,0),
null ))))
Edit I just looked it up. The LEAST function takes multiple arguments:
LEAST( NULLIF(one,0), NULLIF(two,0), NULLIF(three,0), NULLIF(four,0))
Edit 2 I see you want both min and max. Obviously you'd just change LEAST to GREATEST or MIN to MAX as needed.
This may be more straightforward or you may not have a handy least function.
SELECT
f.id, f.title,
(
SELECT MIN(NULLIF(val, 0))
FROM
(
SELECT one AS val UNION ALL
SELECT two UNION ALL
SELECT three UNION ALL
SELECT four
) AS vals
)
) as minval
FROM First f INNER JOIN Second s on f.id = s.first_id
You haven't specified if it's possible for all four columns to be null/0. We may need to tweak for that case.
You can use IFNULL():
WHERE IFNULL(fieldname, 0) != 0

Categories