How can I select the current holder for each championship? - mysql

I want to select the current holders for each championship in a championships table, and return NULL for championships that have not had any winners yet.
Here are the create statements for the two tables:
CREATE TABLE `championships` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`friendly_name` varchar(255) NOT NULL,
`rank` int(2) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
UNIQUE KEY `friendly_name` (`friendly_name`)
) ENGINE=InnoDB;
CREATE TABLE `title_history` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`championship` int(10) unsigned NOT NULL,
`winner` varchar(255) NOT NULL,
`date_from` date NOT NULL,
`location` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `championship` (`championship`)
) ENGINE=InnoDB;
ALTER TABLE `title_history` ADD CONSTRAINT `title_history_ibfk_1` FOREIGN KEY (`championship`) REFERENCES `championships` (`id`) ON UPDATE CASCADE;
What MySQL statement would return the data set I wanted?

Assuming you're storing the winner of a championship as the primary key/id of the holder, something like this should work. You might want to add in another join to get the actual name of the team from another table though.
Because LEFT join will only select rows from the 'right' table when there is a match, everything that doesn't have one should come back as NULL.
SELECT name, [holder]
FROM championships AS c
LEFT JOIN title_history AS h ON c.winner = h.id
EDITED VERSION:
With further insight into your tables and from your comment, maybe try this subselect:
SELECT friendly_name,
(SELECT winner FROM title_history WHERE championship = c.id ORDER BY date_from DESC LIMIT 1)
FROM championships AS c
ORDER BY name
If I understand your structure correctly, that ought to get the last winner of each championship?

Related

I need to optimize tables and queries

I have 3 tables: info, data, link, there is a request for data:
select *
from data,link,info
where link.info_id = info.id and link.data_id = data.id
offer optimization options:
a) tables
b) request.
Queries for creating tables:
CREATE TABLE info (
id int(11) NOT NULL auto_increment,
name varchar(255) default NULL,
desc text default NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251;
CREATE TABLE data (
id int(11) NOT NULL auto_increment,
date date default NULL,
value INT(11) default NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251;
CREATE TABLE link (
data_id int(11) NOT NULL,
info_id int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=cp1251;
Thanks!
Never use commas in the FROM clause. Always use proper, explicit, standard, readable JOIN syntax:
select *
from data d join
link l
on l.data_id = d.id join
info i
on l.info_id = i.id;
Second, for this query your indexes are probably fine. I would also recommend a primary key index on link:
CREATE TABLE link (
data_id int(11) NOT NULL,
info_id int(11) NOT NULL,
PRIMARY KEY (data_id, info_id)
);
This is a good idea in general, even if it is not specific to this query.

How to use JOIN in advance MySQL query

So I have been struggling with this question for a while, and trying to figure out the best way to use JOIN to get the answer of finding the "CertCount" per planet. I know that it wants to have it GROUP BY planets, but I have no clue where planets comes from. Here is the question and code below:
Find the number of certifications held by people grouped by planet. This should have two columns the first, "name" will be the names of planets that have at least one certification. The second column should be "CertCount" and will be the number of certifications held by people from that planet for example if Lee is certified in "Viper" and "Mechanic" and Kara is certified in "Viper" and they are both from Caprica, then the "CertCount" for caprica should be 3:
CREATE TABLE `bsg_cert` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
CREATE TABLE `bsg_cert_people` (
`cid` int(11) NOT NULL DEFAULT '0',
`pid` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`cid`,`pid`),
KEY `pid` (`pid`),
CONSTRAINT `bsg_cert_people_ibfk_1` FOREIGN KEY (`cid`) REFERENCES `bsg_cert` (`id`),
CONSTRAINT `bsg_cert_people_ibfk_2` FOREIGN KEY (`pid`) REFERENCES `bsg_people` (`id`)
) ENGINE=InnoDB
CREATE TABLE `bsg_people` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`fname` varchar(255) NOT NULL,
`lname` varchar(255) DEFAULT NULL,
`homeworld` int(11) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `homeworld` (`homeworld`),
CONSTRAINT `bsg_people_ibfk_1` FOREIGN KEY (`homeworld`) REFERENCES `bsg_planets` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB
CREATE TABLE `bsg_planets` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`population` bigint(20) DEFAULT NULL,
`language` varchar(255) DEFAULT NULL,
`capital` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB
So for the moment: I have the following:
SELECT bsg_planets.name ,
COUNT(*) AS CertCount
FROM bsg_cert_people people_cert
I know that I am missing some code, but I'm not sure where to go from here, and could use a little nudge in the right direction.
You need to join the table according to their primary and foreign key and then do the GROUP BY
SELECT ps.id,
ps.name ,
COUNT(distinct *) AS CertCount
FROM bsg_cert_people cp
JOIN bsg_people pe ON cp.pid = pe.id
JOIN bsg_planets ps ON pe.homeworld = ps.id
GROUP BY ps.id, ps.name
All you need is to do inner join among all 3 tables based on common ids of tables and then group by with Planet id.
Following query should work:
SELECT ps.name ,
count(cert.cid) AS CertCount
FROM bsg_cert_people cert
JOIN bsg_people people ON cert.pid = people.id
JOIN bsg_planets planet ON people.homeworld = planet.id
GROUP BY plsnet.id
having count(distinct *) > 0;
Hope it helps!

How can I query for rows with latest date and do an inner join on a second table?

All the examples I've seen show how to do an inner join using an alias to get rows with the latest date. I can do that with my data but I also want to do an inner join on another table and can't figure how to do both with the same query.
Here are the two tables:
CREATE TABLE `titles` (
`titleID` int(11) unsigned NOT NULL AUTO_INCREMENT,
`titlename` tinytext NOT NULL,
`url` varchar(255) DEFAULT '',
`category` int(2) unsigned NOT NULL,
`postdate` date NOT NULL,
PRIMARY KEY (`titleID`),
KEY `category` (`category`),
CONSTRAINT `titles_ibfk_1` FOREIGN KEY (`category`) REFERENCES `categories` (`catid`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
CREATE TABLE `stats` (
`statid` int(11) unsigned NOT NULL AUTO_INCREMENT,
`score` decimal(3,2) DEFAULT NULL,
`views` int(11) unsigned DEFAULT NULL,
`favs` int(11) DEFAULT NULL,
`comments` int(11) DEFAULT NULL,
`updatedate` date NOT NULL,
`title` int(11) unsigned NOT NULL,
PRIMARY KEY (`statid`),
KEY `title` (`title`),
CONSTRAINT `stats_ibfk_1` FOREIGN KEY (`title`) REFERENCES `titles` (`titleID`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1;
My goals:
1) I want a query that gives me all the latest stats for each title.
2) I want to see the text name of the title (from the titles table).
I can use this query to get the latest score for each title.
select t.score, t.views, t.favs, t.comments, t.updatedate, t.title
from stats t
inner join (
select title, max(updatedate) as updatedate
from stats
GROUP BY title
) tm on t.title = tm.title and t.updatedate = tm.updatedate
But the problem with this query is that it displays the title column from stats which is an int. I want the text name of the title.
I can do this to get the title name and the score, but then I'm not getting the row with the latest date.
select titlename, score, updatedate
from stats
inner join titles
on titleid = title
How can I write a query that achieves both my goals?
You need to join the title table in this case as
select
s1.score,
s1.views,
s1.favs,
s1.comments,
s1.updatedate,
t.titlename
from titles t
join stats s1 on s1.title = t.titleID
join (
select title, max(updatedate) as updatedate
from stats
GROUP BY title
) s2 on s2.title = s1.title and s1.updatedate = s2.updatedate

How to optimize this query as the in array seems to slow things down significantly

I am looking to find out the best way to optimize a query like this:
SELECT
a.ID,
a.ECPCodeID,
a.RegDate,
a.BusName,
a.City,
a.AccountNum,
b.ID as RepCodeID,
b.RepCode
FROM ECPs_Registration a,
Reps_Codes b
WHERE (SUBSTR(a.PostalCode,1,5)IN(SELECT
SUBSTR(Zip,1,5)
FROM Reps_Zip
WHERE RepCodeID = b.ID)
AND a.AccountNum NOT IN(SELECT
ShipTo
FROM Reps_ShipTo))
OR a.AccountNum IN(SELECT
ShipTo
FROM Reps_ShipTo
WHERE RepCodeID = b.ID)
ORDER BY b.RepCode,a.BusName,a.City
I know there are more factors involved such as indexes and such, I just am asking about the query part of it for now. Mainly, since I have to go through the Reps_ShipTo and Reps_Zip tables for tons of records. I thought about changing something like:
a.AccountNum NOT IN (SELECT ShipTo FROM Reps_ShipTo)
INTO
(SELECT count(*) FROM Reps_ShipTo WHERE a.AccountNum = ShipTo) = 0
Not sure if that is proper or if there is a better way. Any help would be appreciated. Thanks.
EDIT:
Schema:
CREATE TABLE IF NOT EXISTS `ECPs_Codes` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`ECPCode` char(4) NOT NULL,
PRIMARY KEY (`ID`),
KEY `ECPCode` (`ECPCode`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ;
CREATE TABLE IF NOT EXISTS `ECPs_Registration` (
`RegDate` datetime NOT NULL,
`ID` int(10) NOT NULL AUTO_INCREMENT,
`ECPCodeID` int(11) NOT NULL,
`FirstName` varchar(200) NOT NULL,
`LastName` varchar(200) NOT NULL,
`BusName` varchar(200) NOT NULL,
`Address` varchar(200) NOT NULL,
`Address2` varchar(200) NOT NULL,
`City` varchar(100) NOT NULL,
`Province` char(2) NOT NULL,
`Country` varchar(100) NOT NULL,
`PostalCode` varchar(10) NOT NULL,
`Email` varchar(200) NOT NULL,
`AccountNum` int(8) NOT NULL,
PRIMARY KEY (`ID`),
KEY `ECPCodeID` (`ECPCodeID`),
KEY `PostalCode` (`PostalCode`),
KEY `AccountNum` (`AccountNum`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `Reps_Codes` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(50) NOT NULL,
`RepCode` varchar(16) NOT NULL,
`AllAccess` tinyint(4) NOT NULL,
PRIMARY KEY (`ID`),
KEY `RepCode` (`RepCode`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `Reps_ShipTo` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`RepCodeID` int(11) NOT NULL,
`ShipTo` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`ID`),
KEY `RepID` (`RepCodeID`),
KEY `ShipTo` (`ShipTo`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE IF NOT EXISTS `Reps_Zip` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`RepCodeID` int(11) NOT NULL,
`Zip` varchar(10) NOT NULL,
PRIMARY KEY (`ID`),
KEY `RepCodeID` (`RepCodeID`),
KEY `Zip` (`Zip`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
There are two things that massively hurt performance on your query.
You are joining two tables by combining multiple conditions, each needing subqueries
You're doing a join on two tables using SUBSTR(Zip,1,5)=SUBSTR(postalcode,1,5)
The logic behind your query seems to be something like:
For every ECPs_Registration find the matching record in Rep_Codes
using the following rules:
If there is a matching record in Reps_ShipTo, to for that registration, use that table to look it up (primary match)
If there isn't a matching record in Reps_ShipTo, seek through Reps_Zip for a matching RepCode by Zipcode-match (secondary)
Now if the above fully describes your situation, you should probably start off by redesigning your database.
The Reps_ShipTo table creates a 0:N relationship between ECPs_Registration and Rep_Codes. Such relations don't need an extra table - they can simply be stored as nullable foreign keys - in your case a RepCodeId in ECPs_Registration would do the trick, and would remove the entire Reps_ShipTo table from the database.
You should probably also create (yes, redundant) extra columns that only store the first 5 letters of the zip codes in both ECPs_Registration and Reps_Zip. This will allow simple equality matches instead of the SUBSTR-functions. Or, you might decide to do this match only once for every record, and store the result in above RepCodeId, which totally eliminates the dual join.
The following query assumes you for some reason don't want to or can't change your database:
SELECT
a.ID, a.ECPCodeID, a.RegDate, a.BusName, a.City, a.AccountNum,
CASE (b1.ID IS NOT NULL, b1.ID, b2.ID) as RepCodeID,
CASE (b1.ID IS NOT NULL, b1.RepCode, b2.RepCode) as MyRepCode
FROM ECPs_Registration a
LEFT JOIN Reps_ShipTo ON (Reps_ShipTo.Shipto=a.AccountNum)
LEFT JOIN Rep_Codes b1 ON (b1.ID=Reps_ShipTo.RepCodeId)
LEFT JOIN Reps_Zip ON (SUBSTR(Zip,1,5)=SUBSTR(a.postalcode,1,5))
LEFT JOIN Rep_Codes b2 ON (b2.ID=Reps_Zip.RepCodeID)
ORDER BY MyRepCode,a.BusName,a.City
Without your database schema and sample data, I have no way to test if above query actually works and has the same result as your original.
SELECT
a.ID,
a.ECPCodeID,
a.RegDate,
a.BusName,
a.City,
a.AccountNum,
b.ID as RepCodeID,
b.RepCode
FROM ECPs_Registration a, Reps_Codes b
INNER JOIN Reps_Zip as r on SUBSTR(a.PostalCode,1,5) = SUBSTR(r.Zip,1,5)
LEFT JOIN Reps_ShipTo as rs on a.AccountNum = rs.ShipTo
LEFT JOIN ShipTo as s on a.AccountNum = s.ShipTo
WHERE (s.id is null or rs.id is null)
ORDER BY b.RepCode,a.BusName,a.City

mysql join, need suggestion

I have created a MySQL table with a foreign key value. I want to use MySQL join to fetch foreign key value.
I have a table called employee and foreign key value sex, Sex table contain Male & Female.
this is my simple join query which is working:
SELECT * FROM sex JOIN employee ON employee.sex_id=sex.id
but i want to use join query here, but it seems I am missing some thing, Please complete this:
SELECT employee_id, first_name, last_name, sex_id FROM employee
And please tell me how i will insert to forigen key in single query for this table:
CREATE TABLE IF NOT EXISTS `employee` (
`employee_id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(20) NOT NULL,
`middle_name` varchar(20) DEFAULT NULL,
`last_name` varchar(20) NOT NULL,
`employee_employee_id` int(11) DEFAULT NULL,
`address_id` int(11) DEFAULT NULL,
`phone_id` int(11) DEFAULT NULL,
`dob` date DEFAULT NULL,
`maritial_id` int(11) NOT NULL,
`sex_id` int(11) NOT NULL,
PRIMARY KEY (`employee_id`),
KEY `fk_employee_employee` (`employee_employee_id`),
KEY `fk_employee_address1` (`address_id`),
KEY `fk_employee_phone1` (`phone_id`),
KEY `fk_employee_maritial1` (`maritial_id`),
KEY `fk_employee_sex1` (`sex_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
I have not good idea in MySQL
SELECT emp.employee_id, emp.first_name, emp.last_name, emp.sex_id, sx.sex_label
FROM employee AS emp
JOIN sex AS sx
ON emp.sex_id = sx.id
Where sex_label is a field name in sex table that would contain either Male or Female.
Try something like:
SELECT e.employee_id, e.first_name, e.last_name, s.id
FROM employee AS e
JOIN sex AS s
ON e.sex_id = s.id