different values in same column, output in different columns - mysql

Currently I am working on a project, which has to do with Formula 1.
That's my structure of the table for results.
CREATE TABLE IF NOT EXISTS `races_results` (
`resultid` int(11) NOT NULL,
`seasonyear` int(4) NOT NULL,
`trackid` tinyint(2) NOT NULL,
`raceid` int(2) NOT NULL,
`session` tinyint(1) NOT NULL,
`q` int(11) NOT NULL,
`place` tinyint(2) NOT NULL,
`driverid` int(2) NOT NULL,
`teamid` int(2) NOT NULL,
`time` int(11) NOT NULL,
`laps` int(2) NOT NULL,
`status` varchar(3) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
My big problem is that I don't get the result in output as I want.
SELECT place, driverid, teamid, if(q=1, time, '') as time1, if(q=2, time, '') as time2, if(q=3, time, '') as time3
FROM `races_results`
WHERE `seasonyear` = 2015 AND `raceid` = 3 AND `session` = 2 AND `q` IN (1,2,3)
GROUP BY driverid
ORDER BY CASE WHEN q = 3 THEN place >= 1 AND place <= 10 END ASC, CASE WHEN q = 2 THEN place >= 11 AND place <= 16 END ASC, CASE WHEN q = 1 THEN place >= 17 AND place <= 22 END ASC
My target is that I want that the all times of a driver will show side by side and after this should be ordered by the participants of the sections.
After this I should have an output like this http://www.formula1.com/content/fom-website/en/championship/results/2015-race-results/2015-japan-results/qualifying.html

From your question I understand that the table races_results has a line for each result, so the times of the different qualifications are on different lines. To get these on one line you can do a join of the same table:
SELECT place, driverid, teamid, r1.time as time1, r2.time as time2, r3.time as time3
FROM races_results r1 LEFT JOIN races_results r2 on (r1.driverid=r2.driverid and r1.raceid=r2.raceid)
LEFT JOIN races_results r3 on (r1.driverid=r3.driverid and r1.raceid=r3.raceid)
WHERE r1.q=1 AND r2.q=2 AND r3.q=3 AND
`seasonyear` = 2015 AND `raceid` = 3 AND `session` = 2
GROUP BY driverid
ORDER BY place;
I assume:
that there is always a result for q=1;
the driverid and raceid are unique for a race on a specific year for a driver;
you want to order by place.

Related

How to get a result with some user generated columns by SQL Query?

I have a Mysql database, and I have 2 tables which schema is:
CREATE TABLE IF NOT EXISTS `items` (
`id` int(10) unsigned NOT NULL,
`name` varchar(200) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `sold_records` (
`id` int(10) unsigned NOT NULL,
`item_id` int(10) NOT NULL,
`count` int(10) NOT NULL,
`sold_at` timestamp NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
As you see, I have some items and some sold records, I want to get such a result by SQL query:
item_name sold_count_at_today sold_count_at_yestoday ... sold_count_at_today - 30day
item_1 x x ... x
item_2 x x ... x
...
item_n x x ... x
Could anyone please give some suggestions about getting such a result by SQL query?
You may debug it in that SQLFiddle.
Unfortunately, I think you would have to do this with a bunch of case functions, like this:
SELECT i.name,
CASE WHEN CAST(sold_at AS date) = '2017-10-31' THEN 'X' ELSE NULL END AS '2017-10-31',
CASE WHEN CAST(sold_at AS date) = '2017-11-01' THEN 'X' ELSE NULL END AS '2017-11-01'
...
FROM items i
INNER JOIN sold_records r
ON i.id = r.item_id
I tried this on the fiddle you set up. You can check it out here:
http://sqlfiddle.com/#!9/92f796/19
select a.name as item_name,
count(b.sold_at = (DATE_FORMAT(FROM_UNIXTIME(current_timestamp), '%e %b %Y'))) as
sold_count_at_today,
count((b.sold_at = (DATE_FORMAT(FROM_UNIXTIME(current_timestamp -1), '%e %b %Y')))) as sold_count_at_yesterday,
count((b.sold_at = (DATE_FORMAT(FROM_UNIXTIME(current_timestamp- 30), '%e %b %Y')))) as 30_day
from items a, sold_records b
where a.id = b.item_id
group by a.name;
Try this. http://sqlfiddle.com/#!9/92f796/23/0 assuming today is current timestamp.

Matching fighters from a single table

I have a table with fighter records and I need to match them accordingly by level, weight, sex and type of fight sport. This is the structure of the table
CREATE TABLE `tfc_sparring_requests_athletes` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`fullname` varchar(300) DEFAULT NULL,
`birthdate` date DEFAULT NULL,
`weight` float DEFAULT NULL,
`fightsport` int(11) DEFAULT NULL,
`sex` int(11) DEFAULT NULL,
`level` int(11) DEFAULT NULL,
`teamid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8
I tried to make a query like this in order to match the fighters
select a.fullname,b.fullname,a.weight
from tfc_sparring_requests_athletes a ,
tfc_sparring_requests_athletes b
where a.weight = b.weight
and a.fightsport = b.fightsport
and a.level =b.level
and a.sex = b.sex
and a.fullname != b.fullname
and the result is
fighter23 fighter1 78
fighter6 fighter5 70
fighter5 fighter6 70
fighter1 fighter23 78
fighter26 fighter25 57
fighter25 fighter26 57
fighter28 fighter27 80
fighter27 fighter28 80
As you can see fighter 1 is matched with fighter 23 in row 1 but in row 4 fighters appear again. Also other fighters repeat their match as well like fighter 5 and 6 in row 2 and 3. How can I avoid this repetition so I can show the matches uniquely?
Anyway I found the solution based on this question How to select distinct pairs in MySQL join (same table) with transitivity?
select MIN(a.id),b.id,a.fullname name1,b.fullname name2,a.weight
from tfc_sparring_requests_athletes a ,tfc_sparring_requests_athletes b
where a.weight = b.weight
and a.fightsport = b.fightsport
and a.level =b.level
and a.sex = b.sex
and a.fullname != b.fullname
AND b.id > a.id
GROUP BY b.id;

Trying to get 2 sums within one table with addition of a join

I've got a submissions table and in it are submissions that have the type either tip or request.
I'm trying to grab all the submissions of a particular user (to display as an aggregation of all their activity on their dashboard).
E.g.
You have submitted: 5 requests and 1 tip.
My submissions create table looks like this:
Table: submissions
Create Table: CREATE TABLE `submissions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`slug` varchar(255) NOT NULL,
`description` mediumtext NOT NULL,
`user_id` int(11) NOT NULL,
`created` datetime NOT NULL,
`type` enum('tip','request') NOT NULL,
`thumbnail` varchar(64) CHARACTER SET latin1 DEFAULT NULL,
`removed` tinyint(1) unsigned NOT NULL DEFAULT '0',
`keywords` varchar(255) NOT NULL,
`ip` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
FULLTEXT KEY `search` (`title`,`description`,`keywords`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
I came up with one query that works and gives me the amount of the users' submissions, but because each submission (row that comes back) saves the type as either tip or request. So I'm trying to figure out how to aggregate that info now.
My query which returns the user with all tips. I'm trying to do one for requests as well.
SELECT users.*, count(submissions.id)
AS "tipsCount"
FROM users
LEFT JOIN submissions on users.id = submissions.user_id
WHERE username = 'blahbster'
AND submissions.type = 'tip'
ORDER BY submissions.created DESC
LIMIT 1;
Perhaps I could use a sum here? My attempt:
SELECT users.*,
SUM(case when type = 'tip' then 1 else 0 end) as "tipsCount"
SUM(case when type = 'request' then 1 else 0 end) as "requestsCount"
FROM users
LEFT JOIN submissions on users.id = submissions.user_id
WHERE username = 'blahbster'
ORDER BY submissions.created DESC
LIMIT 1;
SELECT a.username, b.type,
SUM(case when b.type = 'tip' then 1 else 0 end) as "tipsCount",
SUM(case when b.type = 'request' then 1 else 0 end) as "requestsCount"
FROM users as a
LEFT JOIN submissions as b
ON a.id = b.user_id
GROUP BY a.username, b.type;
The second query you had was close... but it wasn't aggregating a particular user's totals tips and total requests. The sum didn't compute across anything, ie, there was no GROUP BY. The query above should help. You can obviously add the WHERE filter back in if you need it.

Select top N rows out of M groups

I have this table:
CREATE TABLE IF NOT EXISTS `catalog_sites` (
`id` int(10) unsigned NOT NULL auto_increment,
`cat_id` int(10) unsigned NOT NULL,
`date` datetime NOT NULL,
`url` varchar(255) NOT NULL,
`title` varchar(255) NOT NULL,
`description` varchar(255) NOT NULL,
`keywords` varchar(255) NOT NULL,
`visited` int(10) unsigned NOT NULL,
`shown` int(10) unsigned NOT NULL,
`meta_try` int(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `url` (`url`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
I think my problem is simple, but cant seem to find an appropriate solution..
So, this is a table with web-sites, I would like to get 6 sites in 6 different categories (cat_id, total: 36 rows) with the highest rating for each category. The rating is calculated as visited / shown.
I should get 36 rows containing 6 top categories (we can find them by sorting with AVG(visited / shown) ), and 6 top sites in each of these 6 categories.
If you have any ideas how this might happen differently, please tell me.
This should get you what you want using MySQL Variables, the inner query will pre-calculate the rank of visited / shown, and using an order by by the condition you want... Per Category, the highest ranks... and then using #vars will keep the #RankSeq sequentially 1-? per category. From that Prequery (aliased PQ), the OUTER query just queries the PreQuery where the URL's Rank Sequence is <= 6
To further ensure you are only getting the top 6 categories, the inner PreQuery also has a pre-query / limit for the "TopCategories" (alias)
select
PQ.URL,
PQ.Cat_ID,
PQ.Rank,
PQ.URLRankSeq
from
( select
CS.cat_id,
(CS.visited / CS.shown ) as Rank,
CS.url,
#RankSeq := if( #LastCat = CS.Cat_ID, #RankSeq +1, 1 ) URLRankSeq,
#LastCat := CS.Cat_ID as ignoreIt
from
( select cat_id,
avg( visited / shown )
from catalog_sites
group by 1
order by 2 desc
limit 6 ) TopCategories
JOIN catalog_sites CS
on TopCategories.Cat_ID = CS.Cat_ID,
(select #RankSeq := 0, #LastCat = 0 ) SQLVars
order by
CS.cat_id,
Rank ) PQ
where
PQ.URLRankSeq <= 6
I've tried your example, but it doesn't really work for me, or I just don't know how to adapt it to my case. Anyway, I'm still a noob as far as SQL goes, so I couldn't understand your query.
I have managed to solve my problem however. It's complicated and probably the worst possible approach. It is slow too, but I'll cache the results, so that shouldn't be a problem.
Here is my solution:
SET #site_limit = 2;
SET #cat_limit = 6;
SET #row = 0;
SET #limiter = 0;
SET #last_cat = 0;
SELECT `cat_id`, `url`, `visited` / `shown` AS `rating`, #limiter := IF(#last_cat = `cat_id`, IF(#limiter >= #site_limit - 1, #limiter, #limiter + 1), 0) AS `limiter`, #last_cat := `cat_id` AS `last_cat`
FROM `catalog_sites`
WHERE `cat_id`
IN (
SELECT `cat_id`
FROM (
SELECT `cat_id`, #row := #row + 1 AS `row`
FROM (
SELECT `cat_id`
FROM `catalog_sites`
GROUP BY `cat_id`
ORDER BY AVG(`visited` / `shown`) DESC
) AS derived1
) AS derived2
WHERE `row` <= #cat_limit
)
GROUP BY `cat_id`, `limiter`
ORDER BY `cat_id`, `rating` DESC

MySQL joins involving aggregate data

What I require is a SQL query which can report on data from aggregate and singular tables. The current database I have is as follows.
CREATE TABLE IF NOT EXISTS `faults_days` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`employee_id` int(11) NOT NULL,
`day_date` date NOT NULL,
`actioned_calls_out` int(11) NOT NULL,
`actioned_calls_in` int(11) NOT NULL,
`actioned_tickets` int(11) NOT NULL,
)
CREATE TABLE IF NOT EXISTS `faults_departments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(40) NOT NULL,
)
CREATE TABLE IF NOT EXISTS `faults_employees` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`team_id` int(11) NOT NULL,
`name` varchar(127) NOT NULL,
)
CREATE TABLE IF NOT EXISTS `faults_qos` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`qos_date` datetime NOT NULL,
`employee_id` int(11) NOT NULL,
`score` double NOT NULL,
)
CREATE TABLE IF NOT EXISTS `faults_teams` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`department_id` int(11) NOT NULL,
`name` varchar(40) NOT NULL,
)
A row in Day tracks a single employee's performance for a single day (number of calls taken, number of tickets actioned). A Qos is a measure of an employee's quality on a day (there can be more than one Qos per day - what I need to obtain is the average score). Also, a Qos can be performed on a day where the employee has no performance entry in the database, and this will still need to be shown on the report.
The required end result is 4 reports, which show the employee performance grouped by different columns. A breakdown of a single employee's performance per day, an employee's total performance over a period of time, a team's performance over a period of time, and a whole department's performance over a period of time.
My problem, is that my current queries are a little convoluted, and require two separate queries for the Day data, and the Qos data. My PHP application then combines the data before outputting the report. What I would like, is a single query which returns both total performance, and average quality scores.
The current queries I have to show employee performances are:
SELECT
`Employee`.`name` ,
`Team`.`name` ,
`Department`.`name` ,
SUM( `Day`.`actioned_calls_in` ) + SUM( `Day`.`actioned_calls_out` ) ,
SUM( `Day`.`actioned_tickets` )
FROM
`faults_days` AS `Day`
JOIN
`faults_employees` AS `Employee` ON `Day`.`employee_id` = `Employee`.`id`
JOIN
`faults_teams` AS `Team` ON `Employee`.`team_id` = `Team`.`id`
JOIN
`faults_departments` AS `Department` ON `Team`.`department_id` = `Department`.`id`
WHERE
`Day`.`day_date` >= '2011-06-01'
AND `Day`.`day_date` <= '2011-06-07'
GROUP BY `Employee`.`id`
WITH ROLLUP
and
SELECT
`Employee`.`name` ,
`Team`.`name` ,
`Department`.`name` ,
COUNT( `Qos`.`score` ) ,
AVG( `Qos`.`score` )
FROM
`faults_qos` AS `Qos`
JOIN
`faults_employees` AS `Employee` ON `Qos`.`employee_id` = `Employee`.`id`
JOIN
`faults_teams` AS `Team` ON `Employee`.`team_id` = `Team`.`id`
JOIN
`faults_departments` AS `Department` ON `Team`.`department_id` = `Department`.`id`
WHERE
`Qos`.`qos_date` >= '2011-06-01'
AND `Qos`.`qos_date` <= '2011-06-07'
GROUP BY `Employee`.`id`
WITH ROLLUP
I have also tried simply joining the Qos table, but because it returns multiple rows it messes up the SUM() totals, and also has problems due to the missing FULL OUTER JOIN functionality.
EDIT:
I've made some small progress with this. It looks like using subqueries is the way to go, but everything I'm doing is pure guesswork. Here's what I've got so far, its only showing a row if there's an entry in both the Day and Qos tables, which is not what I want, and I've no idea how to expand it to include the various groupings described above.
SELECT
`Employee`.`name` ,
`Team`.`name` ,
`Department`.`name`,
`Day`.`Calls`,
`Day`.`Tickets`,
`Qos`.`NumQos`,
`Qos`.`Score`
FROM `faults_employees` AS `Employee`
JOIN
`faults_teams` AS `Team` ON `Employee`.`team_id` = `Team`.`id`
JOIN
`faults_departments` AS `Department` ON `Team`.`department_id` = `Department`.`id`
JOIN
(SELECT
`Day`.`employee_id` AS `eid`,
SUM(`Day`.`actioned_calls_in`) + SUM(`Day`.`actioned_calls_out`) AS `Calls`,
SUM(`Day`.`actioned_tickets`) AS `Tickets`
FROM `faults_days` AS `Day`
WHERE
`Day`.`day_date` = '2011-03-02'
GROUP BY `Day`.`employee_id`
) AS `Day`
ON `Day`.`eid` = `Employee`.`id`
JOIN
(SELECT
`Qos`.`employee_id` AS qid,
COUNT(`Qos`.`id`) AS `NumQos`,
AVG(`Qos`.`score`) AS `Score`
FROM `faults_qos` AS `Qos`
WHERE
`Qos`.`qos_date` = '2011-03-02'
GROUP BY `Qos`.`employee_id`
) AS `Qos`
ON `Qos`.`qid` = `Employee`.`id`
GROUP BY `Employee`.`id`
You do want the left joins on the fault_qos and fault_days subqueries. That's what will give you a result even if there isn't a corresponding row in one or both. A left join says that the value is necessary in the table(s) to the left of the join that are involved in the join but not the one on the right. I haven't tested this, and it's late, so I might not be thinking clearly, but if you change your query to this it should work:
SELECT
`Employee`.`name` ,
`Team`.`name` ,
`Department`.`name`,
`Day`.`Calls`,
`Day`.`Tickets`,
`Qos`.`NumQos`,
`Qos`.`Score`
FROM `faults_employees` AS `Employee`
JOIN
`faults_teams` AS `Team` ON `Employee`.`team_id` = `Team`.`id`
JOIN
`faults_departments` AS `Department` ON `Team`.`department_id` = `Department`.`id`
LEFT JOIN
(SELECT
`Day`.`employee_id` AS `eid`,
SUM(`Day`.`actioned_calls_in`) + SUM(`Day`.`actioned_calls_out`) AS `Calls`,
SUM(`Day`.`actioned_tickets`) AS `Tickets`
FROM `faults_days` AS `Day`
WHERE
`Day`.`day_date` = '2011-03-02'
GROUP BY `Day`.`employee_id`
) AS `Day`
ON `Day`.`eid` = `Employee`.`id`
LEFT JOIN
(SELECT
`Qos`.`employee_id` AS qid,
COUNT(`Qos`.`id`) AS `NumQos`,
AVG(`Qos`.`score`) AS `Score`
FROM `faults_qos` AS `Qos`
WHERE
`Qos`.`qos_date` = '2011-03-02'
GROUP BY `Qos`.`employee_id`
) AS `Qos`
ON `Qos`.`qid` = `Employee`.`id`
GROUP BY `Employee`.`id`