I want to return the personal best for a user, by class & round; but the Date Shot is coming out incorrect. Help please - so frustrating!
SELECT
c.Class,
r.Round,
h.shootdate as 'Date Shot',
max(h.Score) AS 'Personal Best'
FROM history h, classes c, rounds r
WHERE c.id = h.classid AND r.id = h.roundid AND h.userid = 1
GROUP BY c.Class, r.Round
You could use a self join on history table to pick a row with maximum score for each user per classid and roundid
SELECT
c.Class,
r.Round,
h.shootdate as 'Date Shot',
h.Score AS 'Personal Best'
FROM history h
JOIN (
SELECT classid, roundid, max(score) score
FROM history
WHERE userid = 1
GROUP BY classid, roundid
) h1 ON h.classid = h1.classid AND h.roundid = h1.roundid AND h.score = h1.score
JOIN classes c ON c.id = h.classid
JOIN rounds r ON r.id = h.roundid
-- WHERE h.userid = 1 // not necessary
In your query you are picking shootdate which is not present in group by that is why you are not getting correct value where score is max, Also use explicit join syntax to relate your tables
I have 3 tables (stars mach the ids from the table before):
product:
prod_id* prod_name prod_a_id prod_b_id prod_user
keywords:
key_id** key_word key_prod* kay_country
data:
id dat_id** dat_date dat_rank_a dat_traffic_a dat_rank_b dat_traffic_b
I want to run a query (in a function that gets a $key_id) that outputs all these columns but only for the last 2 dates(dat_date) from the 'data' table for the key_id inserted - so that for every key_word - I have the two last dat_dates + all the other variables included in my SQL query:
So... This is what I have so far. and I don't know how to get only the MAX vars. I tried using "max(dat_date)" in different ways that didn't work.
SELECT prod_id, prod_name, prod_a_id, prod_b_id, key_id, key_word, kay_country, dat_date, dat_rank_a, dat_rank_b, dat_traffic_a, dat_traffic_b
FROM keywords
INNER JOIN data
ON keywords.key_id = data.dat_id
INNER JOIN prods
ON keywords.key_prod = prods.prod_id
Is there a possability to do this with only one query?
EDIT (FOR IgorM):
public function newnew() {
$query = $this->db->query('WITH CTE AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY dat_id ORDER BY dat_date ASC) AS
RowNo FROM data
)
SELECT *
FROM CTE
INNER JOIN keywords
ON keywords.key_id = CTE.dat_id
INNER JOIN prods
ON keywords.key_prod = prods.prod_id
WHERE RowNo < 3
');
$result = $query->result();
return $result;
}
This is the error on the output:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'CTE AS ( SELECT *, ROW_NUMBER() OVER (' at line 1
WITH CTE AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY dat_id ORDER BY dat_date ASC) AS RowNo FROM data ) SELECT * FROM CTE INNER JOIN keywords ON keywords.key_id = CTE.dat_id INNER JOIN prods ON keywords.key_prod = prods.prod_id WHERE RowNo < 3
For SQL
WITH CTE AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY dat_id ORDER BY dat_date ASC) AS
RowNo FROM data
)
SELECT *
FROM CTE
INNER JOIN keywords
ON keywords.key_id = CTE.dat_id
INNER JOIN prods
ON keywords.key_prod = prods.prod_id
WHERE RowNo < 3
For MySQL (not tested)
SET #row_number:=0;
SET #dat_id = '';
SELECT *,
#row_number:=CASE WHEN #dat_id=dat_id THEN #row_number+1 ELSE 1 END AS row_number,
#dat_id:=dat_id AS dat_id_row_count
FROM data d
INNER JOIN keywords
ON keywords.key_id = d.dat_id
INNER JOIN prods
ON keywords.key_prod = prods.prod_id
WHERE d.row_number < 3
The other approach is self joining. I don't want to take credit for somebody else's job, so please look on the following example:
ROW_NUMBER() in MySQL
Look for the following there:
SELECT a.i, a.j, (
SELECT count(*) from test b where a.j >= b.j AND a.i = b.i
) AS row_number FROM test a
If you only want to do this for one key_id at a time (as alluded to in your responses to other answers) and only want two rows, you can just do:
SELECT p.prod_id,
p.prod_name,
p.prod_a_id,
p.prod_b_id,
k.key_id,
k.key_word,
k.key_country,
d.dat_date,
d.dat_rank_a,
d.dat_rank_b,
d.dat_traffic_a,
d.dat_traffic_b
FROM keywords k
JOIN data d
ON k.key_id = d.dat_id
JOIN prods p
ON k.key_prod = p.prod_id
WHERE k.key_id = :key_id /* Bind in key id */
ORDER BY d.dat_date DESC
LIMIT 2;
Whether you want this depends on your data structure and whether there is more than one key/prod combination per date.
Another option limiting just the data rows would be:
SELECT p.prod_id,
p.prod_name,
p.prod_a_id,
p.prod_b_id,
k.key_id,
k.key_word,
k.key_country,
d.dat_date,
d.dat_rank_a,
d.dat_rank_b,
d.dat_traffic_a,
d.dat_traffic_b
FROM keywords k
JOIN (
SELECT dat_id,
dat_date,
dat_rank_a,
dat_rank_b,
dat_traffic_a,
dat_traffic_b
FROM data
WHERE dat_id = :key_id /* Bind in key id */
ORDER BY dat_date DESC
LIMIT 2
) d
ON k.key_id = d.dat_id
JOIN prods p
ON k.key_prod = p.prod_id;
If you want some kind of grouped results for all the keywords, you'll need to look at the other answers.
I think a window function is the best way to go. without knowing a lot about the structure of the data you can try a subquery of what you are trying to restrict and then joining that to the rest of the data. Then within the where clause restrict the rows you pull back.
select p.prod_id, p.prod_name, p.prod_a_id, p.prod_b_id,
t.key_id, t.key_word, t.kay_country, t.dat_date,
t.dat_rank_a, t.dat_rank_b, t.dat_traffic_a, t.dat_traffic_b
from
(
select
k.key_id, k.key_word, k.kay_country, d.dat_date, d.dat_rank_a,
d.dat_rank_b, d.dat_traffic_a, d.dat_traffic_b,
row_number() over (partition by dat_id order by dat_date desc) as 'RowNum'
from keywords as k
inner join
data as d on k.key_id = d.dat_id
) as t
inner join
prods as p on t.key_prod = p.prod_id
where tmp.RowNum <=2
This is a "groupwise max" problem. Reference. CTE does not exist in MySQL.
I'm not totally clear on how your tables are linked, but here is a stab:
SELECT
*
FROM
( SELECT #prev := '', #n := 0 ) init
JOIN
( SELECT #n := if(k.key_id != #prev, 1, #n + 1) AS n,
#prev := k.key_id,
d.*, k.*, p.*
FROM data d
JOIN keywords k ON k.key_id = d.dat_id
JOIN prods p ON k.key_prod = p.prod_id
ORDER BY
k.key_id ASC,
d.dat_date ASC
) x
WHERE n <= 2
ORDER BY k.key_id, n;
you can use this query:
select prod_id, prod_name, prod_a_id, prod_b_id, key_id, key_word,
kay_country, dat_date, dat_rank_a, dat_rank_b, dat_traffic_a, dat_traffic_b
from keywords where dat_date in (
SELECT MAX(dat_date) FROM keywords temp_1
where temp_1.prod_id = keywords.prod_id
union all
SELECT MAX(dat_date) FROM keywords
WHERE dat_date NOT IN (SELECT MAX(dat_date ) FROM keywords temp_2 where
temp_2.prod_id = keywords.prod_id)
)
I have my query determined:
SELECT *
FROM `participation`
LEFT JOIN parties ON parties.id = participation.party_id
WHERE `riding_id` = 10001
AND `election_id` = 41
ORDER BY num_votes DESC
LIMIT 1
This accurately produces the result I want.
The result is the most voted for party.
Now I want to perform this same query on every row of a TABLE ridings
which contains all the riding_id rows. Having some trouble getting it.
I don't want to join the other table - but go through every row and perform the same calculation as above - on each row.
Something like:
SELECT *
FROM `participation`
LEFT JOIN parties ON parties.id = participation.party_id
WHERE `riding_id` = "LOOP ALL riding_id IN ridings TABLE"
AND `election_id` = 41
ORDER BY num_votes DESC
LIMIT 1
Any help would be appreciated.
It is tempting to just use a subquery:
SELECT *
FROM participation LEFT JOIN
parties
ON parties.id = participation.party_id
WHERE riding_id IN (SELECT riding_id FROM ridings) AND
election_id = 41
ORDER BY num_votes DESC
But, you no longer get the top vote getter. You get everything.
Here is a method using variables to get just the top vote getting for each riding_id:
SELECT *
FROM (SELECT *,
(#rn := if(#r = riding_id, #rn + 1,
if(#rn := riding_id, 1, 1)
)
) as seqnum
FROM participation LEFT JOIN
parties
ON parties.id = participation.party_id CROSS JOIN
(SELECT #rn := 0, #r := -1) params
WHERE riding_id IN (SELECT riding_id FROM ridings) AND
election_id = 41
ORDER BY riding_id, num_votes DESC
) pp
WHERE seqnum = 1;
Some time ago I needed a little help here to build a custom query. And this query worked fine till now.
When I run the query (in a procedure) I get the error:
Error Code: 2013. Lost connection to MySQL server during query
My access to my.ini via ssh is read only (because my db is in a shared host "godaddy") so I can't increase the execution time (actual is 60)
Is there one way to optimize this query to make it more fast?
The query is:
SELECT #curRank := #curRank + 1 as rank, p.nick,(kills + ((p.vpos - p.vneg)*5) + (top * 5) - deaths) as score
FROM (SELECT
(SELECT uuid FROM players WHERE players.uuid = p.uuid LIMIT 1) as uuid,
(SELECT nick FROM nicks n WHERE n.pid = p.id ORDER BY id DESC LIMIT 1) as nick,
(SELECT COUNT(*) FROM kills k WHERE k.pid = p.id ) as kills,
(SELECT COUNT(*) FROM deaths d WHERE d.pid = p.id ) as deaths,
(SELECT COUNT(*) FROM headshots h WHERE h.pid = p.id ) as hs,
(SELECT COUNT(*) FROM votos vp WHERE vp.vid = p.id AND tipo="p") as vpos,
(SELECT COUNT(*) FROM votos vn WHERE vn.vid = p.id AND tipo="n") as vneg,
(SELECT COUNT(*) FROM top_rounds t WHERE t.pid = p.id ) as top,
(SELECT #curRank := 0) as rank
FROM players p
) p ORDER BY score DESC LIMIT 30;
Note: all pid's and p.id's already are indexes
Untested (due to lack of sample data):
SELECT p.nick,
(IFNULL(k.cnt, 0)
+ ((IFNULL(vpos.cnt, 0) - IFNULL(vneg.cnt, 0))*5)
+ (IFNULL(t.cnt, 0) * 5) - IFNULL(d.cnt, 0) AS score
FROM players p
LEFT JOIN (
SELECT pid, COUNT(*) AS cnt
FROM kills
GROUP BY pid
) AS k ON p.id = k.pid
⋮
LEFT JOIN (
SELECT pid, COUNT(*) AS cnt
FROM top_rounds
GROUP BY pid
) AS t ON p.id = t.pid
ORDER BY score DESC
LIMIT 30
i.e. make sure each inner query runs once only for all the players. Each subquery results in a table which maps player id to corresponding count. Since there might be zero matching rows, we have to use LEFT JOIN and translate NULL into 0 using IFNULL(foo.cnt, 0).
If you need to index rows, you can add an extra outer query for that alone, but personally I'd prefer to handle that outside SQL in the application which processes the query result.
Is it possible to return the row number as an ID?
In my view where i merge my tables I can't make a unique ID because I need to create left
joins to store every value:
CREATE VIEW
sphinx
AS
SELECT
company.company_id,
company.company_name,
company.company_keywords,
company_address.address_street,
company_address.address_number,
company_address.address_telephone,
company_address.address_fax,
company_address.address_email,
company_address.address_website,
company_address.address_authorized,
company_contact.contact_name,
company_contact.contact_surname,
company_contact.contact_telephone,
company_contact.contact_mobilephone,
company_contact.contact_fax,
company_contact.contact_email,
company_page.page_content,
company_page.page_description,
company_page.page_keywords
FROM company
LEFT JOIN company_address ON company.company_id = company_address.company_id
LEFT JOIN company_page ON company.company_id = company_page.company_id
LEFT JOIN company_contact ON company.company_id = company_contact.company_id
When I receive the row number i can just do SELECT * FROM sphinx LIMIT 1 OFFSET rowNumber
to get the information of that row.
Thanks for your time.
You can do it the following way
SELECT * FROM
(
SELECT
(#rownum:=#rownum + 1) as rownumber, q.*
FROM (
SELECT
* /*shortened for readability*/
FROM company
LEFT JOIN company_address ON company.company_id = company_address.company_id
LEFT JOIN company_page ON company.company_id = company_page.company_id
LEFT JOIN company_contact ON company.company_id = company_contact.company_id
, (select #rownum:=0) r
) q
) asdf
WHERE rownumber BETWEEN 2 AND 3 /*or whatever*/
EDIT: To explain a bit more:
This one
, (select #rownum:=0) r
declares and initializes the variable which will serve as our rownumber.
If you would include #rownum in the SELECT list of your inner most query, you would get a column which has the value 0 in each row.
So you make this inner query a subquery and just add 1 to the #rownum variable.
With only this
SELECT
(#rownum:=#rownum + 1) as rownumber, q.*
FROM (
SELECT
* /*shortened for readability*/
FROM company
LEFT JOIN company_address ON company.company_id = company_address.company_id
LEFT JOIN company_page ON company.company_id = company_page.company_id
LEFT JOIN company_contact ON company.company_id = company_contact.company_id
, (select #rownum:=0) r
) q
you would already have a rownumber. Since you can't refer to the column alias rownumber in the WHERE clause, you would have to write something like
WHERE (#rownum:=#rownum + 1) BETWEEN 2 AND 3 /*or whatever*/
but this would lead to false results, because the #rownum variable would get incremented again and your rownumber would be like
2
4
6
and so on. Therefore you have to put it in another subquery and then you can "limit" your query via the WHERE clause.
Any more questions?