Mysql Many to Many with Count and math - sql

I have a many to many table setup in my mysql database. Teams can be in many games and each game has 2 teams. There is a table in between them called teams_games.
What I am looking to do is create stats for each team. An ideal printout would be:
team_id, team_name, wins, losses, draws, error
My problem is linking the math of which team is the home or away and if they won, then counting those up. I would then have to sum those with the count of times each team was away and won. Then finally join everything together. My current query structure(which doeasn't work correctly) is below along with the Create Table information. Any thoughts?
SELECT t.*, COUNT(g_wins.home_score > g_wins.away_score) AS wins,
COUNT(g_wins.home_score < g_wins.away_score) AS losses
FROM teams as t
JOIN teams_games AS t_g_wins ON t_g_wins.tid = t.tid
JOIN games AS g_wins on t_g_wins.gid = g_wins.gid"
Table Create Table
teams CREATE TABLE `teams` (
`tid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(60) NOT NULL,
`league` varchar(2) NOT NULL,
`active` tinyint(1) NOT NULL,
PRIMARY KEY (`tid`)
)
CREATE TABLE teams_games (
`tid` int(10) unsigned NOT NULL,
`gid` int(10) unsigned NOT NULL,
`homeoraway` tinyint(1) NOT NULL,
PRIMARY KEY (`tid`,`gid`),
KEY `gid` (`gid`),
CONSTRAINT `teams_games_ibfk_1` FOREIGN KEY (`tid`) REFERENCES `teams` (`tid`),
CONSTRAINT `teams_games_ibfk_2` FOREIGN KEY (`gid`) REFERENCES `games` (`gid`)
)
CREATE TABLE games (
`gid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`location` varchar(60) NOT NULL,
`time` datetime NOT NULL,
`description` varchar(400) NOT NULL,
`error` smallint(2) NOT NULL,
`home_score` smallint(2) DEFAULT NULL,
`away_score` smallint(2) DEFAULT NULL,
PRIMARY KEY (`gid`)
)

You are complicating things: this should be a 2-n not n-m relationship.
Get rid of teams_games table and make 2 fields to games table, ex: home_tid and away_tid
EDIT: and the query would be then something similar:
select t.tid, t.name,
sum(g.home_score < g.away_score xor t.tid = g.home_tid) wins,
sum(g.home_score > g.away_score xor t.tid = g.home_tid) losses,
sum(g.home_score = g.away_score) draws
from games g
join teams t on t.tid = g.home_tid or t.tid = g.away_tid
group by t.tid
so the answer is to use sum, otherwise it counts both true and false values in output which is total rows resulting from joins.
EDIT2:
select t.tid, t.name,
sum(g.home_score < g.away_score xor tg.homeoraway) wins,
sum(g.home_score > g.away_score xor tg.homeoraway) losses,
sum(g.home_score = g.away_score) draws
from games g
join team_games tg on tg.gid = g.gid
join teams t on t.tid = tg.tid
group by t.tid

Related

SQLite: Get Output From Two Tables Using Common Reference ID

I am new in SQLite and i have been working on an issue for quite a long time.
Lets say we have 2 database table say tbl_expense and tbl_category. Please find below the following table structure.
tbl_category
CREATE TABLE IF NOT EXISTS tbl_category(
category_id INTEGER PRIMARY KEY AUTOINCREMENT,
category_name VARCHAR(20) DEFAULT NULL,
category_desc VARCHAR(500) DEFAULT NULL,
category_icon VARCHAR(100) DEFAULT NULL,
category_created timestamp default CURRENT_TIMESTAMP
)
tbl_expense
CREATE TABLE IF NOT EXISTS tbl_expense(
expense_id INTEGER PRIMARY KEY AUTOINCREMENT,
expense_name VARCHAR(20) DEFAULT NULL,
expense_desc VARCHAR(500) DEFAULT NULL,
expense_type VARCHAR(20) DEFAULT NULL,
expense_amt DECIMAL(6.3) DEFAULT NULL,
expense_date TIMESTAMP DEFAULT NULL,
expense_category INTEGER DEFAULT NULL,
expense_created_date timestamp DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (expense_category) REFERENCES tbl_category(category_id)
ON DELETE SET NULL
)
Assume we have data in the tables like this below.
Expected Output:
Assure we have category_id and expense_category as common fields. How can i create an SQL Query where i can list all categories and sum of their expense amount as follows.
Please help me on this issue.
You need an INNER join of the tables and aggregation:
SELECT c.category_name Category,
SUM(e.expense_amt) Amount
FROM tbl_category c INNER JOIN tbl_expense e
ON e.expense_category = c.category_id
GROUP BY c.category_id;
If you want all categories from the table tbl_category, even those that are not present in tbl_expense, use a LEFT join and TOTAL() aggregate function:
SELECT c.category_name Category,
TOTAL(e.expense_amt) Amount
FROM tbl_category c LEFT JOIN tbl_expense e
ON e.expense_category = c.category_id
GROUP BY c.category_id;

How to select just parent and not inherit values

I have 3 tables. Game, FinalGame which extends Game and Participant who has list of game and list of FinalGame. Now I have problem. How to select just game and not finalGame. How to select Participant with just game and not finalGame.
In first option I tried:
select * from game g where {g.ID not in (select pl.game_id from PLAY_OFF_TABLE pl) }
but I am not sure if it is right.
In second option I tried;
select * from participant p left join game g1 on p.ID = g1.HOME_PARTICIPANT_ID
but this select all games including final. I have no idea how I can exclude final game
CREATE TABLE IF NOT EXISTS `GAME` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`HOME_PARTICIPANT_ID` int(11) DEFAULT NULL,
`AWAY_PARTICIPANT_ID` int(11) DEFAULT NULL,
`STATUS` enum('WIN','LOSE','DRAW') DEFAULT NULL,
`RESULT` varchar(50) DEFAULT NULL,
PRIMARY KEY (`ID`),
KEY `HOME_PARTICIPANT_ID` (`HOME_PARTICIPANT_ID`),
KEY `AWAY_PARTICIPANT_ID` (`AWAY_PARTICIPANT_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=10 ;
CREATE TABLE IF NOT EXISTS `PLAY_OFF_GAME` (
`GAME_ID` int(11) NOT NULL,
`GROUP_ID` int(11) NOT NULL,
`POSITION` int(11) NOT NULL,
PRIMARY KEY (`GAME_ID`),
KEY `GROUP_ID` (`GROUP_ID`),
KEY `POSITION` (`POSITION`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `PARTICIPANT` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
...
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=10 ;
UPDATE:
select * from PARTICIPANT p left join GAME g1 on p.ID = g1.HOME_PARTICIPANT_ID join PLAY_OFF_GAME pl on g1.id = pl.GAME_ID where p.group_id = 441 and pl.GAME_ID is null
retun 0 rows
select * from PARTICIPANT p left join GAME g1 on p.ID = g1.HOME_PARTICIPANT_ID left join PLAY_OFF_GAME pl on g1.id = pl.GAME_ID where p.group_id = 441 and pl.GAME_ID is null
return 5
select * from PARTICIPANT p left join GAME g1 on p.ID = g1.HOME_PARTICIPANT_ID left join PLAY_OFF_GAME pl on g1.id = pl.GAME_ID where p.group_id = 441
return all participant 8 but also final game which I want to exclude
Your first query would select GAME rows that do not have a corresponding PLAY_OFF_GAME, but a join is probably much quicker:
SELECT *
FROM GAME g
LEFT JOIN PLAY_OFF_GAME pog ON g.ID=pog.GAME_ID
WHERE pog.GAME_ID IS NULL;
For the second question, you can do the same trick:
SELECT * FROM (
(SELECT p1.*, g.ID
FROM PARTIPANT p1
LEFT JOIN GAME g1 ON p1.ID=g1.HOME_PARTICIPANT_ID
LEFT JOIN PLAY_OFF_GAME pog1 ON g1.ID=pog1.GAME_ID
WHERE pog1.GAME_ID IS NULL)
UNION
(SELECT p2.*, NULL
FROM PARTICIPANT p2
JOIN GAME g2 ON p2.ID=g2.HOME_PARTICIPANT_ID
JOIN PLAY_OFF_GAME pog2 ON g2.ID=pog2.GAME_ID)) AS tmp;
Query may have typos. First selects Particpants in a game, or in no game, but it's excluding the participants only in final games. The second select fills those back in, ignoring the game.
Also:
To select all participants, and not just 'home' participants, not in a game or in a non-playoff game, add OR p.ID=g.AWAY_PARTICIPANT_ID to the ON clause. (I'm following your example, but the description seems to be at odds.
Should there be a foreign key constraint on PLAY_OFF_GAME? E.g., CONSTRAINT play_off_game_fk FOREIGN KEY (GAME_ID) REFERENCES GAME (ID)?

SQLite3 INNER JOIN with 3 tables

I'm trying to select from 3 tables with an INNER JOIN:
The tables:
CREATE TABLE tracks (
'track_id' INTEGER PRIMARY KEY NOT NULL,
'name' TEXT NOT NULL,
'length' REAL DEFAULT '0.00',
'city' TEXT
);
CREATE TABLE heats (
'heat_id' INTEGER PRIMARY KEY NOT NULL,
'track_id' INTEGER UNSIGNED NOT NULL,
'heat_pos' INTEGER UNSIGNED NOT NULL,
'day_pos' INTEGER UNSIGNED NOT NULL,
'type' TEXT NOT NULL DEFAULT 'training',
'average' REAL,
'date' TEXT,
'comment' TEXT,
FOREIGN KEY ('track_id') REFERENCES tracks ('track_id')
);
CREATE TABLE laps (
'lap_id' INTEGER PRIMARY KEY NOT NULL,
'heat_id' INTEGER UNSIGNED NOT NULL,
'laptime' REAL UNSIGNED NOT NULL,
FOREIGN KEY ('heat_id') REFERENCES heats ('heat_id')
);
When selecting information from 2 tables (laps and heats) it works like I expected:
select
laps.lap_id,
laps.laptime,
heats.heat_pos
from laps
inner join heats on laps.heat_id = heats.heat_id;
But now I want to select the corresponding tracknames from the track table:
select
laps.lap_id,
laps.laptime,
heats.heat_pos,
tracks.name
from laps, tracks, heats
inner join heats on laps.heat_id = heats.heat_id and
inner join heats on tracks.track_id = heats.track_id;
This gives me the following error:
ambiguous column name: heats.heat_pos
I'm completely lost, but I have a feeling it's just a small mistake.
Anyone knows what I'm doing wrong?
select
laps.lap_id,
laps.laptime,
heats.heat_pos,
tracks.name
from laps
inner join heats on laps.heat_id = heats.heat_id
inner join tracks on tracks.track_id = heats.track_id;
select
laps.lap_id,
laps.laptime,
heats.heat_pos,
tracks.name
from laps
inner join heats on laps.heat_id = heats.heat_id
inner join heats on tracks.track_id = heats.track_id
ORDER BY laps.lap_id

Two problems with my query: Show null values and order by before group by

I'm having major problems with my query. I want to show all results in the source table even if there is no pricing entry in the right table.
My order by is also not working. I want to order by product_pricing.PP_CashPrice prior to grouping by.
Here is my SQL code:
SELECT * FROM source
LEFT JOIN product_pricing ON source.Source_ID = product_pricing.Source_ID
WHERE (product_pricing.Product_ID = '234'
OR product_pricing.PP_ID = NULL)
AND source.Source_Active = 'Yes'
GROUP by source.Source_ID
ORDER by PP_CashPrice desc
I basically need it to show all sources. The right column will have duplicates but I only need to show the highest one.
My right column is as follows:
CREATE TABLE product_pricing ( PP_ID int(10) NOT NULL AUTO_INCREMENT, PP_Type varchar(150) NOT NULL, PP_CashPrice decimal(10,2) NOT NULL, PP_DateObtained date NOT NULL, PP_TimeObtained time NOT NULL, PP_Active varchar(3) NOT NULL, PP_Postcode varchar(150) NOT NULL, Source_ID int(10) NOT NULL, SC_ID int(10) NOT NULL, Product_ID int(10) NOT NULL, PRIMARY KEY (PP_ID) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
You should not use a where clause on a "Left joined" table. Put the condition in the where clause
I would also use a COALESCE operator for the ordering clause, and probably add an ordering on s.Source_ID if you want different sourceId with "inner pricing" ordering.
SELECT * FROM source s
LEFT JOIN product_pricing pp ON s.Source_ID = pp.Source_ID AND pp.PP_ID = '234'
AND s.Source_Active = 'Yes'
GROUP by s.Source_ID
ORDER by s.Source_ID, COALESCE(p.PP_CashPrice, 0) desc

Help with complex join conditions

I have the following mysql table schema:
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
--
-- Database: `network`
--
-- --------------------------------------------------------
--
-- Table structure for table `contexts`
--
CREATE TABLE IF NOT EXISTS `contexts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`keyword` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
-- --------------------------------------------------------
--
-- Table structure for table `neurons`
--
CREATE TABLE IF NOT EXISTS `neurons` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
-- --------------------------------------------------------
--
-- Table structure for table `synapses`
--
CREATE TABLE IF NOT EXISTS `synapses` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`n1_id` int(11) NOT NULL,
`n2_id` int(11) NOT NULL,
`context_id` int(11) NOT NULL,
`strength` double NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
What SQL can I write to get all Neurons associated with a specified context along with the sum of the strength column for the synapses associated with each neuron?
I'm using the following query, which returns the sum of the strength of the synapses associated with one neuron. I need to get information for all of the neurons:
/* This query finds how strongly the neuron with id 1 is connected to the context with keyword ice cream*/
SELECT SUM(strength) AS Strength FROM
synapses
JOIN contexts AS Context ON synapses.context_id = Context.id
JOIN neurons AS Neuron ON Neuron.id = synapses.n1_id OR Neuron.id = synapses.n2_id
WHERE Neuron.id = 1 AND Context.keyword = 'ice cream'
For example, that query returns one row, where Strength is 2. Ideally, I could have one column for the neurons.id, one for neurons.name, and one for SUM(synapses.strength) with one record for each distinct neuron.
Use:
SELECT DISTINCT
n.*,
COALESCE(x.strength, 0) AS strength
FROM NEURONS n
JOIN SYNAPSES s ON n.id IN (s.n1_id, s.n2_id)
JOIN CONTEXTS c ON c.id = s.context_id
LEFT JOIN (SELECT c.id AS c_id,
n.id AS n_id,
SUM(strength) AS Strength
FROM SYNAPSES s
JOIN CONTEXTS c ON c.id = s.context_id
JOIN NEURONS n ON n.id IN (s.n1_id, s.n2_id)
GROUP BY c.id, n.id) x ON x.c_id = c.id
AND x.n_id = n.id
Does this do what you want?
SELECT contexts.keyword, neurons.id, neurons.name, SUM(synapses.strength)
FROM neurons
INNER JOIN synapses ON neurons.id = synapses.n1_id OR neurons.id = synapses.n2_id
INNER JOIN contexts ON synapses.context_id = contexts.id
GROUP BY contexts.keyword, neurons.id, neurons.name