Mysql Select rows based on whether other column is null - mysql

I have a legacty table "wages" that I cannot change and I want to get results from this table such that i get allowedWage for a country based on whether there is a value for farmer or not otherwise get the allowedWage for worker
Input
id country farmer worker allowedWage
1 US 1 null 100
2 US null 1 50
3 AU 1 null 60
4 CA null 1 80
Expected Output
id country allowedWage
1 US 100
3 AU 60
4 CA 80
so I wrote the following query if someone wants to find the wage for country US, AU, CA and IN
select id, country, allowedWage from wages
where country in ('US', 'AU', 'CA', 'IN')
and ((farmer = 1 and worker is null) or (worker = 1 or farmer is null))
but this obviously gives all row and I am trying to figure out if there is a way to exclude worker data for a country if there is value of farmer
Actual Output
id country allowedWage
1 US 100
2 US 50
3 AU 60
4 CA 80

You can do it with conditional aggregation:
SELECT country,
COALESCE(MAX(CASE WHEN farmer THEN allowedWage END), MAX(allowedWage)) allowedWage
FROM wages
GROUP BY COUNTRY
For MySql 8.0+ you can do it with FIRST_VALUE() window function:
SELECT DISTINCT country,
FIRST_VALUE(allowedWage) OVER (PARTITION BY country ORDER BY farmer = 1 DESC) allowedWage
FROM wages
See the demo.
Results:
> country | allowedWage
> :------ | ----------:
> AU | 60
> CA | 80
> US | 100

Hmmm . . . this a prioritization query. You can use not exists to select the workers where necessary:
select id, country, allowedWage
from t
where farmer = 1 or
not exists (select 1
from t t2
where t2.country = t.country and t2.farmer = 1
);
Note that the worker column doesn't seem necessary because all the information needed is in the farmer column.

You could use row_number(), if you are running MySQL 8.0.
select *
from (
select t.*,
row_number() over(partition by country order by (farmer <=> 1) desc) rn
from mytable t
)
where rn = 1

Related

does count automatically sum up similar values without a group by statement

THIS IS THE INPUT
team_1 team_2 winner
Aus India India
Eng NZ NZ
India SL India
SA Eng Eng
SL Aus Aus
OUTPUT
team_name matches_played no_of_wins
India 2 2
SL 2 NULL
SA 1 NULL
Eng 2 1
Aus 2 1
NZ 1 1
This is the MYSQL solution for the problem:
WITH CTE AS (SELECT team_1 team_name,winner FROM icc_world_cup
UNION ALL
SELECT team_2 team_name,winner FROM icc_world_cup)
SELECT DISTINCT team_name, # first column
COUNT(team_name) as Macthes_played, #second column
(SELECT COUNT(*) FROM
(SELECT IF(team_1=winner,team_1,team_2) win_team FROM icc_world_cup )a
WHERE team_name=win_team GROUP BY win_team) no_of_wins #third column
FROM CTE GROUP BY team_name
The above output is what I got from the code which I have written but the problem is
If I remove the GROUP BY statement in the third column that is
GROUP BY win_team
Then the output was something like this
team_name matches_played no_of_wins
India 2 2
SL 2 0
SA 1 0
Eng 2 1
Aus 2 1
NZ 1 1
How the count is able sum up team india's wins that is 2 without a group by statement, does it have something to with the where clause condition and
NOTICE that the NULL values in the third column were replaced by 0's.
How is it possible that without a group by statement my count function is able to sum up similar values and how the null are changed to 0.
I would use a union approach here:
SELECT team_name, COUNT(*) AS matches_played, SUM(win) AS no_of_wins
FROM
(
SELECT team_1 AS team_name, IF(team_1 = winner, 1, 0) AS win FROM yourTable
UNION ALL
SELECT team_2, IF(team_2 = winner, 1, 0) FROM yourTable
) t
GROUP BY team_name;

PHP SQL order by multiple rows

I have a table called 'scorelist' with the following results:
ID USER_ID SCORE SEASON
-----------------------------
1 1 35 3
2 1 45 2
3 2 80 3
4 2 85 1
5 3 65 2
I want to make a score list where I show the scores of the users but only of their last played season.
Result should be:
ID USER_ID SCORE SEASON
-----------------------------
3 2 80 3
5 3 65 2
1 1 35 2
I use the following code:
SELECT * FROM scorelist
WHERE season = (
SELECT season FROM scorelist ORDER BY season DESC LIMIT 1
)
GROUP BY user_id
ORDER BY score DESC;
But then I only get the results of season 3, so a lot of users are not shown.
I also tried:
SELECT * FROM scorelist group by user_id ORDER BY score DESC, season DESC
But this is also not working.
I hope you can help me.
The subquery gets the latest season for each user. If you join to that you get your desired results
SELECT s1.*
FROM scorelist s1
JOIN
(
SELECT user_id, max(season) AS season
FROM scorelist
GROUP BY user_id
) s2 ON s1.user_id = s2.user_id AND s1.season = s2.season
Since MySQL 8.0 you can use window function row_number to solve this problem:
WITH ordered_scorelist AS (
SELECT
scorelist.*,
row_number() over (partition by USER_ID order by SEASON DESC) rn
FROM scorelist
) SELECT
USER_ID, SCORE, SEASON
FROM ordered_scorelist
WHERE rn = 1
ORDER BY SCORE DESC;
MySQL row_number test

configure query to bring rows which have more than 1 entries

How to get those entries which have more than 1 records?
If it doesn't make sense... let me explain:
From the below table I want to access the sum of the commission of all rows where type is joining and "they have more than 1 entry with same downmem_id".
I have this query but it doesn't consider more entries scenario...
$search = "SELECT sum(commission) as income FROM `$database`.`$memcom` where type='joining'";
Here's the table:
id mem_id commission downmem_id type time
2 1 3250 2 joining 2019-09-22 13:24:40
3 45 500 2 egbvegr new time
4 32 20 2 vnsjkdv other time
5 23 2222 2 vfdvfvf some other time
6 43 42 3 joining time
7 32 353 5 joining time
8 54 35 5 vsdvsdd time
Here's the expected result: it should be the sum of the id no 2, 7 only
ie. 3250+353=whatever.
It shouldn't include id no 6 because it has only 1 row with the same downmem_id.
Please help me to make this query.
Another approach is two levels of aggregation:
select sum(t.commission) income
from (select sum(case when type = 'joining' then commission end) as commission
from t
group by downmem_id
having count(*) > 1
) t;
The main advantage to this approach is that this more readily supports more complex conditions on the other members of each group -- such as at most one "joining" record or both "joining" records and no more than two "vnsjkdv" records.
Use EXISTS:
select sum(t.commission) income
from tablename t
where t.type = 'joining'
and exists (
select 1 from tablename
where id <> t.id and downmem_id = t.downmem_id
)
See the demo.
Results:
| income |
| ----- |
| 3603 |
You can use subquery that will find all downmem_id having more than one occurrence in the table.
SELECT Sum(commission) AS income
FROM tablename
WHERE type = 'joining'
AND downmem_id IN (SELECT downmem_id
FROM tablename t
GROUP BY downmem_id
HAVING Count(id) > 1);
DEMO

Get Percentage of Last X entries in MySQL

I have 2 tables in MySQL(InnoDB). The first is an employee table. The other table is the expense table. For simplicity, the employee table contains just id and first_name. The expense table contains id, employee_id(foreign key), amount_spent, budget, and created_time. What I would like is a query that returns the percentage of their budget spent for the most recent X number of expense they've registered.
So given the employee table:
| id | first_name
-------------------
1 alice
2 bob
3 mike
4 sally
and the expense table:
| id | employee_id | amount_spent | budget | created_time
----------------------------------------------------------
1 1 10 100 10/18
2 1 50 100 10/19
3 1 0 40 10/20
4 2 5 20 10/22
5 2 10 70 10/23
6 2 75 100 10/24
7 3 50 50 10/25
The query for the last 3 trips would return
|employee_id| first_name | percentage_spent |
--------------------------------------------
1 alice .2500 <----------(60/240)
2 bob .4736 <----------(90/190)
3 mike 1.000 <----------(50/50)
The query for the last 2 trips would return
|employee_id| first_name | percentage_spent |
--------------------------------------------
1 alice .3571 <----------(50/140)
2 bob .5000 <----------(85/170)
3 mike 1.000 <----------(50/50)
It would be nice if the query, as noted above, did not return any employees who have not registered any expenses (sally). Thanks in advance!!
I'll advise you to convert datatype of created_time as DATETIME in order to get accurate results.
As of now, I've assumed that most recent id indicates most recent spents as it's what sample data suggests.
Following query should work (didn't tested though):
select t2.employee_id,t1.first_name,
sum(t2.amount_spent)/sum(t2.budget) as percentage_spent
from employee t1
inner join
(select temp.* from
(select e.*,#num := if(#type = employee_id, #num + 1, 1) as row_number,
#type := employee_id as dummy
from expense e
order by employee_id,id desc) temp where temp.row_number <= 3 //write value of **n** here.
) t2
on t1.id = t2.employee_id
group by t2.employee_id
;
Click here for DEMO
Feel free to ask doubt(s), if you've any.
Hope it helps!
If you are using mysql 8.0.2 and higher you might use window function for it.
SELECT employee_id, first_name, sliding_sum_spent/sliding_sum_budget
FROM
(
SELECT employee_id, first_name,
SUM(amount_spent) OVER (PARTITION BY employee_id
ORDER BY created_time
RANGE BETWEEN 3 PRECEDING AND 0 FOLLOWING) AS sliding_sum_spent,
SUM(budget) OVER (PARTITION BY employee_id
ORDER BY created_time
RANGE BETWEEN 3 PRECEDING AND 0 FOLLOWING) AS sliding_sum_budget,
COUNT(*) OVER (PARTITION BY employee_id
ORDER BY created_time DESC) rn
FROM expense
JOIN employee On expense.employee_id = employee.id
) t
WHERE t.rn = 1
As mentioned by Harshil, order of row according to the created_time may be a problem, therefore, it would be better to use date date type.

How to group by on a highest value

So, for example i've got the following table;
ID COUNTRY VALUE
--------------------- -------------------- --------------------
1 India 12000
2 India 11000
3 UK 11000
4 India 15000
5 Canada 11000
And I would like to group by Country but only have the country with the highest value show up, if I would just use a group by query like:
SELECT * FROM countries GROUP BY country
I would get;
ID COUNTRY VALUE
--------------------- -------------------- --------------------
1 India 12000
3 UK 11000
5 Canada 11000
Where the value for india would be 12000. I would like the query to group on the highest value for the group by on country like:
ID COUNTRY VALUE
--------------------- -------------------- --------------------
3 UK 11000
4 India 15000
5 Canada 11000
So it's grouped on the highest value which is 15000.
DEMO
SELECT s1.ID, s1.COUNTRY, s1.VALUE
FROM countries s1
LEFT JOIN countries s2
ON s1.VALUE < s2.VALUE
AND s1.COUNTRY = s2.COUNTRY
WHERE s2.COUNTRY IS NULL;
OUTPUT
NOTE: But be carefull of ties. In that case you can get one random from those ties.
You can use the MAX aggregate function.
select
country,
max(value) value
from countries
group by
country
See the live example.
Edit: The original solution was only correct due to the nature of the data. I've removed the ID from the first query, to correct the mistake. Here is another solution (based on #Juan Carlos Oropeza's work - thank you) that will return the ID and eliminate the ties.
select
min(x.id) id,
x.country,
x.value
from (
select
c.*
from countries c
left join countries c1 on c.value < c1.value and c.country = c1.country
where c1.country is null
) x
group by
x.country,
x.value
;
See the live example - I've modified the data to cover edge cases mentioned above.