Mysql tree table join and count Rows - mysql

I have these three tables below. I want to join them using a MySQL query. Below the tables I've provided also how my output should look like.
GameTable:
GameID GameName
1 NvsA
2 NvsB
3 DvsA
4 NvsE
PlayerOnGame:
GameID PlayerName PlayerNumber PlayerID
1 Clement 7 10
1 Niyoyita 8 11
2 Clement 8 10
2 David 6 5
PlayerdataTable:
GameID Action PlayerNumber
1 kick 7
1 pass 8
1 Run 7
1 Kick 7
2 Kick 8
2 Pass 6
2 Run 8
2 Run 8
Output for only PlayerName = Clement:
GameName Action ActionCount
NvsA Kick 2
NvsA Run 1
NvsB Kick 1
NvsB Run 2
Please can someone help me with the query to get this output?

You can use this query to count
SELECT
gt.GameName,
pdt.Action,
count(pdt.Action) AS ActionCount
FROM GameTable AS gt
INNER JOIN PlayerdataTable AS pdt ON pdt.GameID = gt.GameID
INNER JOIN PlayerOnGame AS pg ON pg.GameID = pdt.GameID AND pg.PlayerNumber = pdt.PlayerNumber
WHERE pg.PlayerName = 'Clement'
GROUP BY gt.GameID , pdt.Action

Related

Commonality in many to many relationship SQL query

If I have agents that can have many properties, and vice versa, defined in a in a junction table, how would I query the top agents (an agent who has at least two properties in common with two other agents.)
agent
id
1
2
3
4
5
properties
id
1
2
3
4
5
6
agent_properties
agent_id
property_id
1
1
1
2
1
3
2
2
2
3
3
1
3
3
3
5
4
3
4
4
4
6
5
1
5
2
5
5
6
4
6
6
Example:
Agent 1 (propr1, propr2, propr3),
Agent 2 (propr2, propr3),
Agent 3 (propr1, propr3, propr5)
Agent 4 (propr3, propr4, propr6)
Agent 5 (propr1, propr2, propr5)
Agent 6 (propr4, propr6)
So the query should return Agent 1, Agent 3, Agent 5
If anyone can show how to do this using Laravel Eloquent that would be great.
A solution would be to work with bits.
First you should add a new column in table properties and this new column to be named bin_value. The values from this column must be unique per property and to be power of 2. In your case would be:
id
bin_value
1
1
2
2
3
4
4
8
5
16
6
32
Second, add a new column to agent table named properties and run following update query:
UPDATE agent AS a
INNER JOIN (
SELECT ap.agent_id, SUM(p.bin_value) AS bin_properties
FROM agent_properties AS ap
INNER JOIN properties AS p ON p.id = ap.property_id
GROUP BY ap.agent_id
) AS b ON a.id = b.agent_id
SET a.properties = b.bin_properties
;
Third, run the query with bit function which helps you to find agents which have at least 2 shared properties with at least 2 other agents:
SELECT a1.agent_id
, a1.name
, COUNT(*) AS total_other_agents
FROM agent AS a1
INNER JOIN agent AS a2 ON a1.id != a2.id
AND BIT_COUNT(a1.properties & a2.properties) >= 2 -- at least 2 shared properties
GROUP BY a1.id
HAVING total_other_agents >= 2 -- at least 2 other agents
ORDER BY total_other_agents DESC, a1.name
;
If you have a large number of properties try to set values in bin_value like this:
UPDATE properties
SET bin_value = BINARY(POWER(2, id-1))
;

How to get the average number of cities crossed [duplicate]

This question already has an answer here:
How to write these complex MySQL queries?
(1 answer)
Closed 1 year ago.
I am a beginner at databases. I need to write some SQL queries.
The tables are:
Expedition(id, number, id_captain, id_ship, id_heros)
City(id, name)
Heros(id, family_name, first_name)
Step(id, index, id_expedition, id_city)
sample data :
'Table expedition'
id
number
id_captain
id_ship
id_hero
1
1
1
10
8
2
2
2
1
5
3
3
1
8
3
4
4
10
9
6
5
5
5
7
4
6
6
6
5
4
7
7
7
3
7
8
8
8
2
8
9
9
9
1
3
10
10
1
4
2
11
11
6
3
1
12
12
8
6
1
13
13
5
8
6
14
14
4
9
9
15
15
3
10
4
16
16
10
2
2
17
17
9
3
3
18
18
8
7
7
19
19
9
8
10
20
20
7
2
2
table 'heros'
id
family_name
first_name
1
familyname1
firstname1
2
familyname2
firstname2
3
familyname3
firstname3
4
familyname4
firstname4
5
familyname5
firstname5
6
familyname6
firstname6
7
familyname7
firstname7
8
familyname8
firstname8
9
familyname9
firstname9
10
familyname10
firstname10
query1: The family (based on the family name) with the least travelling (the fewest cities different crossings).
i have done this for the first query:
select expedition.id, id_hero, heros.family_name as Famille_expedition, count(distinct id_city) as city_count
from expedition, step, heros
where expedition.id=step.id and expedition.id_hero=heros.id
group by id_hero
having city_count =
(select count(distinct id_city) as min_city_count
from expedition, step
where expedition.id=step.id
group by id_hero
order by min_city_count asc
limit 1);
query2: The average of cities crossed by an expedition
I have no idea how to answer the second one.
Well, first ask yourself what information do you need to answer your question?
From your question, I'd say the average number of crossings is just the sum of all entries in the steps table, divided by the number of expeditions, since in each step, one city is visited and the average of all visits is what you are looking for:
SELECT (
(SELECT COUNT(s.id_city)
FROM step AS s) /
(SELECT COUNT(e.id)
FROM expedition AS e) ) AS total_average__cities
That being said, it depends on how exactly you define number of cities and crossing. Imagine the following example data for the table step:
id
idx
id_expedition
id_city
1
1
1
1
2
2
1
5
3
3
1
3
4
1
2
5
5
2
2
9
6
1
3
8
7
2
3
5
8
3
3
9
9
4
3
5
10
5
3
8
The table lists the steps for three expeditions. Expedition 1 goes from one city via another to a third. Expedition 2 goes directly from one city to another. And expedition 3 goes through several cities and visits one city twice along the way and also returns to the city that it started in.
The average number of cities over all these steps is (3 + 2 + 5 [cities in all steps]) / 3 [expeditions] = 3.3333. That is the result of the above query.
Now, if you define number of cities as meaning unique cities for each expedition, expedition 3 only visits 3 cities instead of 5. Then your average calculates as (3 + 2 + 3 [unique cities/expedition in all steps]) / 3 [expeditions] = 2.6666. The according query needs to count the distinct cities within each expedition before building the average:
SELECT (
(SELECT SUM(cnt) FROM (SELECT COUNT(DISTINCT s.id_city) AS cnt
FROM step AS s
GROUP BY s.id_expedition) t) /
(SELECT COUNT(e.id)
FROM expedition AS e) ) AS total_average__cities
Now, if you define crossing as only covering cities along the way, expedition 1 only crosses 1 city and expedition 2 crosses none at all.
Then your query also needs to look differently. You need to filter the all cities to exclude the first and the last for each expedition. The subquery could look like this:
SELECT s.* FROM step s
JOIN ( SELECT id_expedition,
MAX(idx) AS max_idx,
MIN(idx) AS min_idx
FROM step s
GROUP BY id_expedition) minmax
ON s.id_expedition = minmax.id_expedition
AND s.idx > minmax.min_idx
AND s.idx < minmax.max_idx
So for the case that you want the number of cities crossed excluding start and stop, your average would be computed as (1 + 0 + 3 [intermediate cities in all steps]) / 3 [expeditions] = 1.3333. The according query would be
SELECT (
(SELECT COUNT(s.id_city)
FROM step s
JOIN ( SELECT id_expedition,
MAX(idx) as max_idx,
MIN(idx) as min_idx
FROM step s
GROUP BY id_expedition) minmax
ON s.id_expedition = minmax.id_expedition
AND s.idx > minmax.min_idx
AND s.idx < minmax.max_idx) /
(SELECT COUNT(e.id)
FROM expedition AS e) ) AS total_average__cities
Finally, in case you want to both exclude start and stop and only want to count unique cities, your average would be computed as (1 + 0 + 2 [unique intermediate cities in all steps]) / 3 [expeditions] = 1. The following query combines the two approaches from above:
SELECT (
(SELECT SUM(cnt) FROM (SELECT COUNT(DISTINCT id_city) AS cnt
FROM step s
JOIN ( SELECT id_expedition,
MAX(idx) AS max_idx,
MIN(idx) AS min_idx
FROM step s
GROUP BY id_expedition) minmax
ON s.id_expedition = minmax.id_expedition
AND s.idx > minmax.min_idx
AND s.idx < minmax.max_idx
GROUP BY s.id_expedition) t) /
(SELECT COUNT(e.id)
FROM expedition AS e) ) AS total_average_cities
You can test all these queries in this db<>fiddle.

Find the lowest value of every row of selected columns

As the result of the query, I want to get all rows (Drivers), order by the drivers who got most series wins.
If a driver has won 4 tacks at least one or more times but failed to win the remaining track at least once, his series count is 0.
Driver Table
ID|Name| .........
1 A
2 B
3 C
4 D
Tracks Table
TID |FK|Track1_Wins|Track2_Wins| Track3_Wins|Track4_Wins|Track5_Wins|
1 1 5 6 3 2 4
2 2 2 4 0 5 3
3 3 6 3 9 4 7
4 4 5 8 2 4 1
My code sample
SELECT `Drivers`.`Name`, LEAST(`Track1_Wins`, `Track2_Wins`, `Track3_Wins`, `Track4_Wins`, `TRACK5_Wins`) AS Series
FROM `Drivers`, `Tracks`
ORDER BY Series DESC;
Accidently I got part expected output when I use WHERE with Driver ID
SELECT `Drivers`.`Name`, LEAST(`Track1_Wins`, `Track2_Wins`, `Track3_Wins`, `Track4_Wins`, `TRACK5_Wins`) AS Series FROM `Drivers`, `Tracks` WHERE `Drivers`.`ID` = 2 ORDER BY Series DESC;
It will give the expected result but with Same Driver Name as expected
B 3
B 2
B 1
B 0
My expected output is
Name | Series
C 3
A 2
D 1
B 0
Run this,
SELECT d.`Name`,
LEAST(`Track1_Wins`, `Track2_Wins`, `Track3_Wins`, `Track4_Wins`, `TRACK5_Wins`) AS Series
FROM `Drivers` d INNER JOIN `Tracks` t
ON t.`FK` = d.`ID`
ORDER BY Series DESC;
This returns the user name associated with the FK. Also, try to use kebab_case and lower case for all your column and table name. Makes it much easier to run the code

How to add rank, based on points in mysql

I am struggle with mysql query. please help me.
This is my query, i getting correct result but i need to modify the result in mysql.
SELECT bu.username,
bg.id as goal_id,
br.id as reason_id,
(SELECT COUNT(test_reason_id) FROM test_rank WHERE test_reason_id = br.id) as point
FROM
test_goal AS bg INNER JOIN test_reason AS br ON
br.user_id=bg.user_id INNER JOIN test_user AS bu ON
br.user_id=bu.id
WHERE
bg.id = br.test_goal_id
GROUP BY
bg.id
ORDER BY
point DESC
Tabble-1
My actual table look like this when i use ORDER BY point DESC then its look like Table-2
username goal_id reason_id point
khan 8 3 2
john 6 9 5
yoyo 5 21 4
smith 11 6 5
Tabble-2
My result set look like this
username goal_id reason_id point
john 6 9 5
smith 11 6 5
yoyo 5 21 4
khan 8 3 2
But i want my result set like this
username goal_id reason_id point rank
john 6 9 5 1
smith 11 6 5 2
yoyo 5 21 4 3
khan 8 3 2 4
is this possible? please can any one help me. it too difficult for me.
Add a row count variable like this:
select a.*, (#row := #row + 1) as rank
from (
SELECT bu.username,
bg.id as goal_id,
br.id as reason_id,
(SELECT COUNT(test_reason_id) FROM test_rank WHERE test_reason_id = br.id) as point
FROM
test_goal AS bg INNER JOIN test_reason AS br ON
br.user_id=bg.user_id INNER JOIN test_user AS bu ON
br.user_id=bu.id
WHERE
bg.id = br.test_goal_id
GROUP BY
bg.id
ORDER BY
point DESC
) a, (SELECT #row := 0) r
See this simplified SQLFiddle example

MYSQL select query using count (*)

i have a problem concerning a select query in MYSQL
i have two different tables and i want to obtain a certain result
i used COUNT method which gave me only the results (>=1)
But in reality , i want to use all counts with zero included how to do it?
My query is:
SELECT
first.subscriber_id,
second.tag_id,
COUNT(*)
FROM
content_hits first
JOIN content_tag second ON first.content_id=second.content_id
GROUP BY
second.Tag_id,first.Subscriber_id<br>
First table:Content_hits
CONTENT_ID SUBSCRIBER_ID
30 1
10 10
34 4
32 2
40 3
28 3
30 6
31 8
12 3
Second table:Content_tag
CONTENT_ID TAG_ID
1 1
2 1
3 1
4 1
5 1
6 1
7 1
8 1
9 1
10 1
11 2
12 2
13 2
14 2
Result but incomplete For example:Subsrciber6 for tag_id=1 should have a count(*)=0
subscriber_id tag_id COUNT(*)
1 1 4
2 1 7
3 1 2
4 1 1
5 1 3
7 1 2
8 1 1
9 1 1
10 1 3
1 2 2
2 2 3
3 2 2
Now that you have further elaborated on what you actually want to achieve, it can be seen that the problem is much more complex. You actually want all combinations of subscriber_id and tag_id, and then count the number of actual entries in the joined table product. whew. So here goes the SQL:
SELECT combinations.tag_id,
combinations.subscriber_id,
-- correlated subquery to count the actual hits by tag/subscriber when joining
-- the two tables using content_id
(SELECT count(*)
FROM content_hits AS h
JOIN content_tag AS t ON h.content_id = t.content_id
WHERE h.subscriber_id = combinations.subscriber_id
AND t.tag_id = combinations.tag_id) as cnt
-- Create all combinations of tag/subscribers first, before counting anything
-- This will be necessary to have "zero-counts" for any combination of
-- tag/subscriber
FROM (
SELECT DISTINCT tag_id, subscriber_id
FROM content_tag
CROSS JOIN content_hits
) AS combinations
Not sure, but is this what you want?
SELECT first.subscriber_id, second.tag_id, COUNT(*) AS c
FROM content_hits first JOIN content_tag second ON first.content_id=second.content_id
GROUP BY second.Tag_id,first.Subscriber_id HAVING c = 0