I'm not a specialist in MySQL and need some help with a query.
table (t_teams)
team | group
Germany | A
Russia | B
Danmark | A
Japan | C
.....
table (t_matches)
id | fk_team1 | fk_team2 | points_team1 | points_team2
1 | Germany | Russia | 3 | 0
2 | Danmark | Japan | 3 | 0
3 | Germany | Japan | 3 | 0
4 | Russia | Danmark | 1 | 1
Foreign keys are:
fk_team1
fk_team2
Now, I want to sum all points in t_matches for each team by a mysql query.
The final result should looks like
**team | points**
Germany | 6
Russia | 1
Danmark | 1
Japan | 0
I tried something like this, but it doesn't work :-(
SELECT
t1.team,
SUM(t2.points_team1) + SUM(t2.points_team2) AS points
FROM t_teams t1, t_matches t2
WHERE (t1.team = t2.fk_team1) OR (t1.team = t2.fk_team2)
Would be great when someone can help me with this simple query.
You need to unpivot the data and then aggregate the results:
select team, sum(points)
from ((select fk_team1 as team, points_team1 as points from t_teams t
) union all
(select fk_team2 as team, points_team2 as points from t_teams t
)
) t
group by team;
Related
I have a table games with values such as:
+----------+------+
| game | year |
+----------+------+
| Football | 1999 |
| Football | 2000 |
| Football | 2001 |
| Football | 2002 |
| Cricket | 1996 |
| Tennis | 2001 |
| Tennis | 2002 |
| Tennis | 2003 |
| Tennis | 2009 |
| Golf | 1994 |
| Golf | 1996 |
| Golf | 1997 |
+----------+------+
I am trying to see if a game has an entry with a minimum three consecutive years in the table. My expected output is:
+----------+
| game |
+----------+
| Football |
| Tennis |
+----------+
Because:
Football has four entries out of which four are consecutive years => 1999, 2000, 2001, 2002
Tennis has four entries out of which three are consecutive years => 2001, 2002, 2003
In order to find the rows with a minimum three consecutive entries I first partitioned the table on game and then checked difference between the current and the next row as below:
select game, year, case
when (year - lag(year) over (partition by game order by year)) is null then 1
else year - lag(year) over (partition by game order by year)
end as diff
from games
Output of the above query:
+----------+------+------+
| game | year | diff |
+----------+------+------+
| Football | 1999 | 1 |
| Football | 2000 | 1 |
| Football | 2001 | 1 |
| Football | 2002 | 1 |
| Cricket | 1996 | 1 |
| Tennis | 2001 | 1 |
| Tennis | 2002 | 1 |
| Tennis | 2003 | 1 |
| Tennis | 2009 | 6 |
| Golf | 1994 | 1 |
| Golf | 1996 | 2 |
| Golf | 1997 | 1 |
+----------+------+------+
I am not able to proceed from here on getting the output by filtering the data for each game with its difference.
Could anyone let me know if I am in the right track of the implementation? If not, how do I prepare the query to get the expected output?
You could use a self join approach here:
SELECT DISTINCT g1.Game
FROM games g1
INNER JOIN games g2
ON g2.Game = g1.Game AND g2.Year = g1.Year + 1
INNER JOIN games g3
ON g3.Game = g2.Game AND g3.Year = g2.Year + 1;
Demo
The above query requires any matching game to have at least one record whose year can be found in the following year, and the year after that as well.
You can use lag() and lead() and compare them to the current Year:
with u as
(select *, case
when lag(Year) over(partition by Game order by Year) = Year - 1
and lead(Year) over(partition by Game order by Year) = Year + 1
then 1 else 0
end as consec
from games)
select distinct Game
from u
where consec = 1;
Fiddle
Yes, your initial approach is correct. You were actually really close to fully figuring it out yourself.
What I would do is alter LAG a bit:
year - LAG(year, 2) OVER (
PARTITION BY game
ORDER BY year
ROWS BETWEEN UNBOUNDED PRECEEDING AND CURRENT ROW
)
For each row, this will compare the difference between the year from current row and the year from (current - 2)th row.
If it is the third consecutive row it will yield 2 which you can filter in where clause.
If your data contains duplicates you need to group by game, year first.
Using CTE(Common Table Expression) and the useful ROW_NUMBER window function this can be easily solved.
WITH CTE (name, RN) AS (
select name, ROW_NUMBER() OVER (PARTITION BY name order by year) RN
from game)
Select Distinct name
from CTE
Where RN >= 3
So my example table is like this -
I have a mysql version 5.7 database which I can connect to. Read-only rights.
My table goes like this:
human_id | dog_id | dog_bought_at | amount_paid_for_dog | purchase_place | buyer_has_criminal_past
1 | 1 | 27-12-2019 | 100 | Tokyo | 0
1 | 2 | 03-01-2020 | 200 | Moscow | 0
2 | 3 | 03-01-2020 | 200 | Los Angeles | 0
3 | 4 | 03-01-2020 | 50 | Washington | 0
3 | 3 | 05-01-2020 | 30 | Dallas | 0
4 | 2 | 06-01-2020 | 150 | Texas | 1
What I need to show is this:
dog_id | last_owner_id | total_amount_paid_for_dog | last_purchase_date | last_purchase_place
1 | 1 | 100 | 27-12-2019 | Tokyo
2 | 4 | 350 | 06-01-2020 | Moscow
3 | 3 | 230 | 05-01-2020 | Dallas
4 | 3 | 50 | 03-01-2020 | Washington
Last_purchase_place is shown only for those humans, which do not have criminal past.
what I have tried:
SELECT
e.dog_id
,MAX(e.human_id) last_owner_id
,SUM(e.amount_paid_for_dog) total_amount_paid_for_dog
,MAX(e.dog_bought_at) last_purchase_date
,e_filter.purchase_place last_purchase_place
FROM example e
LEFT JOIN (
SELECT
dog_id
,dog_bought_at
,purchase_place
,human_id
FROM example
WHERE buyer_has_criminal_past != 1
) e_filter ON e.dog_id = e_filter.dog_id AND e.dog_bought_at = e_filter.dog_bought_at
But I am stuck on the logic, that allows to sum up ALL amounts, yet filter out unneeded values.
This is my first question here, so if this is a duplicate or not well written, please say it. Any help appreciated.
SELECT e1.dog_id,
e1.human_id last_owner_id,
sq1.total_amount_paid_for_dog,
e1.dog_bought_at last_purchase_date,
e2.purchase_place last_purchase_place
FROM example e1
JOIN ( SELECT dog_id,
MAX(dog_bought_at) dog_bought_at,
SUM(amount_paid_for_dog) total_amount_paid_for_dog
FROM example
GROUP BY dog_id ) sq1 ON e1.dog_id = sq1.dog_id
AND e1.dog_bought_at = sq1.dog_bought_at
LEFT JOIN example e2 ON e1.dog_id = e2.dog_id
JOIN ( SELECT dog_id,
MAX(dog_bought_at) dog_bought_at
FROM example
WHERE buyer_has_criminal_past = 0
GROUP BY dog_id ) sq2 ON e2.dog_id = sq2.dog_id
AND e2.dog_bought_at = sq2.dog_bought_at
fiddle
I have two seperate MySQL queries which return two set of results that look like this
Query 1 result:
country | buyers | payment | num_of_sales
UK | 5 | 106.45 | 4
Thailand | 6 | 250.10 | 3
and:
Query 2 result:
country | buyers | payment | num_of_sales
UK | 2 | 150.00 | 1
Norway | 9 | 310.80 | 2
All I need is to merge / union them so the final result will look like this:
Expected result
country | buyers | payment | num_of_sales
UK | 7 | 256.45 | 5
Thailand | 6 | 250.10 | 3
Norway | 9 | 310.80 | 2
Please help, and if possible, with a bit of explaination. Thank you!
Use UNION ALL to merge the two queries results' sets into one result set, then use GROUP BY country with SUM to get the totals you are looking for, something like this:
SELECT
country,
SUM(buyers) AS buyers,
SUM(payment) AS payment,
SUM(num_of_sales) AS num_of_sales
FROM
(
SELECT country, buyers, payment, num_of_sales
FROM -- your query1 results
UNION ALL
SELECT country, buyers, payment, num_of_sales
FROM -- your query2 result2
) AS t
GROUP BY country
SELECT d.mt_code,
d.dep_name,
d.service_name,
COUNT(*)
FROM DepartmentService AS d
LEFT JOIN tbl_outgoing AS t ON d.mt_code = t.depCode
WHERE d.service_type = 'MT'
AND t.smsc = "mobitelMT"
AND t.sendDate BETWEEN '2014-07-01' AND '2014-07-02'
GROUP BY d.mt_code
DepartmentService table has details about departments that offer services. tbl_outgoing table contains all the transactions happened for a particular service which are done by customers. In the WHERE clause two cafeterias should be fulfilled which are service_type = 'MT' and smsc = "newMT". I want to get a report which shows all the departments with the transactions for a given period. I have used a LEFT JOIN because I want to get all the departments. SQL works fine and get the result I want except,
When there are no transactions for a particular service for a particular period, The department is also ignored. What I want to do is show the department in the resultset and COUNT(*) column to be 0.
How can I do that?
The problem could be that you are filtering on the joined table using the where condition which will filter also the department services which don"t have a match in the join, move the filtering in the join and leave only the filters on d in the where clause:
SELECT d.mt_code,
d.dep_name,
d.service_name,
COUNT(t.id)
FROM DepartmentService AS d
LEFT JOIN tbl_outgoing AS t
ON d.mt_code = t.depCode
AND t.smsc = "mobitelMT"
AND t.sendDate BETWEEN '2014-07-01' AND '2014-07-02'
WHERE d.service_type = 'MT'
GROUP BY d.mt_code
To explain why this happens I'll walk you through what happens with your query and with my query, as dataset I'll use this:
states
____ _________
| id | state |
| 1 | Germany |
| 2 | Italy |
| 3 | Sweden |
|____|_________|
cities
____ ________ ___________ ____________
| id | city | state_fk | population |
| 1 | Berlin | 1 | 10 |
| 2 | Milan | 2 | 5 |
|____|________|___________|____________|
First I'll go through your query.
SELECT s.id, s.state, c.population, c.city
FROM states s
LEFT JOIN cities c
ON c.state_fk = s.id
WHERE c.population < 10
So le't go step by step, you select the three states, left join with cities ending up with:
____ _________ ____________ ________
| id | state | population | city |
| 1 | Germany | 10 | Berlin |
| 2 | Italy | 5 | Milan |
| 3 | Sweden | NULL | NULL |
|____|_________|____________|________|
The you filter the population using WHERE c.population < 10, at this point your left with this:
____ _________ ____________ ________
| id | state | population | city |
| 2 | Italy | 5 | Milan |
|____|_________|____________|________|
You loose Germany because Berlin population was 10 but you lost also Sweden which had NULL, if you wanted to keep the nulls you should have specified it in the query:
WHERE (c.population < 10 OR IS NULL c.population)
Which returns:
____ _________ ____________ ________
| id | state | population | city |
| 2 | Italy | 5 | Milan |
| 3 | Sweden | NULL | NULL |
|____|_________|____________|________|
Now my query:
SELECT s.id, s.state, c.population, c.city
FROM states s
LEFT JOIN cities c
ON c.state_fk = s.id
AND c.population < 10
Before joining the two, we filter the table cities (using the AND c.population < 10 condition after the ON), what remains is:
____ ________ ___________ ____________
| id | city | state_fk | population |
| 2 | Milan | 2 | 5 |
|____|________|___________|____________|
Because Milan is the only city with population minor than 10, now we can join the two tables:
____ _________ ____________ ________
| id | state | population | city |
| 1 | Germany | NULL | NULL |
| 2 | Italy | 5 | Milan |
| 3 | Sweden | NULL | NULL |
|____|_________|____________|________|
As you can see the data from the left table stays because the filtering condition was applied only to the cities table.
The result set changes depending on what you want to achieve, if for example you do want to filter Germany because Berlin has population minor than 10 and keep Sweden you should use the first approach adding the IS NULL condition, if you want to keep it instead, you should use the second approach and pre filter the table on the right of the left join.
I have a requirement to map a tax rate to a person based on the country the person was resident at that time.
tbl: person
| p_id | name_first | name_last |
=======++========================
| 1 | john | smith |
| 2 | joanne | smyth |
tbl: person_in_country
| p_id | iso | arrived |
===========================
| 1 | GB | 1980-01-01 |
| 2 | FR | 1987-03-21 |
| 1 | FR | 2003-06-17 |
| 1 | JP | 2008-07-02 |
| 2 | GB | 2008-10-01 |
| 1 | GB | 2009-01-10 |
tbl: country
| iso | ctry_name |
========================
| GB | United Kingdom |
| FR | France |
| JP | Japan |
tbl: tax_rates
| iso | tax_rate | tax_date |
===============================
| GB | 17.5 | 1970-01-01 |
| FR | 15.0 | 1977-03-21 |
| JP | 12.0 | 1977-06-17 |
| FR | 15.0 | 1994-03-21 |
| JP | 18.5 | 2008-07-02 |
| GB | 15 | 2008-04-01 |
| GB | 20 | 2010-05-01 |
So I need tuples containing the person in the country and the tax rate that they should have at a given time..
Something along the lines of:
select p.p_id, p.name_first, p.name_last,
pic.arrived,
c.iso, c.ctry_name,
t.tax_rate
from people p
left join (select * from person_in_country order by arrived desc) pic using (p_id)
left join country c on c.iso = pic.iso
left join (select * from tax_rates order by tax_date desc) t on t.iso = c.iso
where t.tax_date <= NOW()
group by p.pid, pic.arrived, t.tax_date
Hope this make sense... and many thanks in advance
Actually, you have to do a query basicly in three steps. First one you are going to retrieve a kind of "raw data" with all desired columns, joining the relating tables, whatever these columns will be used to join or to retrieve choosen data.
After that, you have to group data in order to turn only last dates from the matching join.
Finally, you have to query again tax table to retrieve tax in the tax date current at the moment of arrival.
It is possible that there is a more easy or ellegant way to do so, but this query is working. Check your system performance depending on the query demmands. It seems a bit hard at a first glance, but it isn't when taking a more carefull look. The SQL code:
SELECT
c02.iso,
c02.p_id,
c02.name_first,
c02.name_last,
c02.ctry_name,
c02.arrived,
c02.mtax_date,
tax_rates.tax_rate
FROM (
SELECT
c01.iso,
c01.p_id,
c01.name_first,
c01.name_last,
c01.ctry_name,
c01.arrived,
Max(c01.tax_date) AS mtax_date
FROM (
SELECT
country.iso,
person.p_id,
person.name_first,
person.name_last,
country.ctry_name,
person_in_country.arrived,
tax_rates.tax_date
FROM
tax_rates
INNER JOIN (
country
INNER JOIN (
person
INNER JOIN
person_in_country
ON
person.p_id = person_in_country.p_id
)
ON
country.iso = person_in_country.iso
)
ON
tax_rates.iso = person_in_country.iso
GROUP BY
country.iso,
person.p_id,
person.name_first,
person.name_last,
country.ctry_name,
person_in_country.arrived,
tax_rates.tax_date
HAVING (((tax_rates.tax_date)<=[arrived]))
) as c01
GROUP BY
c01.iso,
c01.p_id,
c01.name_first,
c01.name_last,
c01.ctry_name,
c01.arrived
) as c02
INNER JOIN
tax_rates ON (
c02.mtax_date = tax_rates.tax_date
)
AND
(
c02.iso = tax_rates.iso
);
Output:
iso p_id name_first name_last ctry_name arrived mtax_date tax_rate
GB 1 john smith United Kindom 01/01/1980 01/01/1970 18
FR 2 joanne smyth France 21/03/1987 21/03/1977 15
FR 1 john smith France 17/06/2003 21/03/1994 15
JP 1 john smith Japan 02/07/2008 02/07/2008 18
GB 1 john smith United Kindom 10/01/2009 01/04/2008 15
GB 2 joanne smyth United Kindom 01/10/2008 01/04/2008 15