Using a query on a joined table instead of actual table - mysql

I have a query now which works great for finding player info including their rank:
SELECT t1.pid,
t1.type,
t1.pspeed,
t1.distance,
t1.maxspeed,
t1.prestrafe,
t1.strafes,
t1.sync,
t1.wpn,
1+SUM(t2.pid IS NOT NULL) as rank
FROM records t1
LEFT JOIN records t2
ON t1.type = t2.type
AND t1.pspeed = t2.pspeed
AND t1.distance < t2.distance
WHERE t1.pid = "'.$pid.'"
AND t1.type IN ("type1","type2","type3")
GROUP BY t1.type, t1.pspeed
The problem now is that I wish to use an authid instead of a pid as my variable. I can do an easy join on the records table along with a player table that has an id equal to the pid in the records table as well as the authid I wish to search by:
SELECT * FROM records JOIN players ON records.pid=players.id
I wish to use this second query in place of the FROM records portion of the first query but I've confused myself in my attempts via the many aliases used.
Here's a pitiful example of an attempt:
SELECT t1.pid,
t1.type,
t1.pspeed,
t1.distance,
t1.maxspeed,
t1.prestrafe,
t1.strafes,
t1.sync,
t1.wpn,
1+SUM(t2.pid IS NOT NULL) as rank
FROM (
SELECT *
FROM records
JOIN players ON records.pid=players.id
) t3 t1
LEFT JOIN records t2
ON t1.type = t2.type
AND t1.pspeed = t2.pspeed
AND t1.distance < t2.distance
WHERE t1.authid = "'.$authid.'"
AND t1.type IN ("type1","type2","type3")
GROUP BY t1.type, t1.pspeed
Anyone able to whip me into shape please feel free to do so. I'm awful at SQL still.

Your Join looks pretty close. However you gave your subquery two aliases. Try just one
SELECT t1.pid, t1.type, t1.pspeed, t1.distance, t1.maxspeed, t1.prestrafe, t1.strafes, t1.sync, t1.wpn, 1+SUM(t2.pid IS NOT NULL) as rank
FROM
(
SELECT players.*, rec.pid, rec.type, rec.pspeed, rec.distance, rec.maxspeed,
rec.prestrafe, rec.strafes, rec.sync, rec.wpn
FROM records AS rec JOIN players ON rec.pid = players.id ) AS t1
LEFT OUTER JOIN records t2
ON t1.type = t2.type AND t1.pspeed = t2.pspeed AND t1.distance < t2.distance
WHERE t1.authid = "'.$authid.'" AND
t1.type IN ("type1","type2","type3")
GROUP BY t1.type, t1.pspeed
Also, naming your columns in your subquery should eliminate the duplicate column error.

Related

MySQL: LIMIT parameter in JOIN producing unexpected results

I have read here that MySQL processes ordering before applying limits. However, I receive different results when applying a LIMIT parameter in conjunction with a JOIN subquery. Here is my query:
SELECT
t1.id,
(t2.counts / c.matches)
FROM
table_one t1
JOIN
table_two t2 ON t1.id = t2.id
JOIN
(
SELECT
t1.id, COUNT(DISTINCT t1.id) AS matches
FROM
table_one t1
JOIN table_two t2 ON t1.id = t2.id
WHERE
t1.id IN (3390 , 3236, 148, 2811, 829, 137)
AND t2.value_one <= 30
AND t2.value_two < 2
GROUP BY t1.id
ORDER BY (t2.counts / matches)
LIMIT 0, 50 -- PROBLEM IS HERE (I think)
) c ON c.id = t1.id
ORDER BY (t2.counts / c.matches), t1.id;
Here is a rough description of what I think is happening:
The sub-query selects a bunch of ids from table_one that meet the criteria
These are ordered by (t2.counts / matches)
The top 50 (in ascending order) are fashioned into a table
This resulting table is then joined on the the id column
Results are returned from the top level JOIN - without a GROUP BY clause this time. table_one is a reference table so this will return many rows with the same ID.
I appreciate that some of these joins don't make a lot of sense but I have stripped down my query for readability - it's normally quite chunky .
The problem is that when, I include the LIMIT parameter I get a different set of results and not just the top 50. What I want to do is get the top results from the subquery and use these to join onto a bunch of other tables based on the reference table.
Here is what I have tried so far:
LIMIT on the outer query (this is undesirable as this cuts off important information).
Trying different LIMIT tables and values.
Any idea what is going wrong, or what else I could try?
I have found a solution to my problem. It seems as if my matches column name does can't be used in my ORDER BY clause - which is weird since I don't get an error. Either way, this solves the problem:
SELECT
t1.id,
(t2.counts / c.matches)
FROM
table_one t1
JOIN
table_two t2 ON t1.id = t2.id
JOIN
(
SELECT
t1.id, COUNT(DISTINCT t1.id) AS matches
FROM
table_one t1
JOIN table_two t2 ON t1.id = t2.id
WHERE
t1.id IN (3390 , 3236, 148, 2811, 829, 137)
AND t2.value_one <= 30
AND t2.value_two < 2
GROUP BY t1.id
ORDER BY (t2.counts / COUNT(DISTINCT t1.id)) -- This line is changed
LIMIT 0, 50
) c ON c.id = t1.id
ORDER BY (t2.counts / c.matches), t1.id;

A query that will see which results are the most

There is a table where strings are stored by type.
How to write 1 query so that it counts and outputs the number of results of each type.
Tried to do this:
SELECT COUNT(t1.id), COUNT(t2.id)
FROM table t1
LEFT JOIN table t2
ON t2.type='n1'
WHERE t1.type='b2';
But nothing works.
There can be many types, how can this be done?
I think this is what you want:
SELECT t1.type, COUNT(DISTINCT t1.id), COUNT(DISTINCT t2.id)
FROM table1 AS t1
LEFT JOIN table2 AS t2 ON t1.type = t2.type
GROUP BY t1.type
Note that this won't show any types that are only in table2. For that you'd need a FULL OUTER JOIN, which MySQL doesn't have. See How can I do a FULL OUTER JOIN in MySQL? for how to emulate it.

Have a left join where duplicates in the second table is involved - MYSQL

Table 1:
user score
------------
A 1
B 2
Table 2:
user comment time
----------------------------
A good <timestamp 1>
A bad <timestamp 2>
B average <timestamp 3>
I want to join these two tables such that I get the below:
user score comment
-------------------------
A 1 good
B 2 average
As you can see I'll need to join the second table's comment based on the timestamp (the most recent timestamp). I tried
SELECT st.user as user,st.score,
case when v.comment is null then 'NA' else v.comment end as comment
FROM tale1
left JOIN (select distinct user,comment,max(time) from table2) v ON st.user=v.user
but this doesnt work.
You can join with a correlated subquery that filters on the latest timestamp:
select
t1.*,
t2.comment
from table1 t1
left join table2 t2
on t2.user = t1.user
and t2.time = (
select max(t22.time)
from table2 t22
where t21.user = t1.user
)
Side note: I am unsure that you do need a left join here (your sample data does not demonstrate that).
You only want one column from table2 so I recommend a correlated subquery:
select t1.*,
(select t2.comment
from table2 t2
where t2.user = t1.user
order by t2.time desc
limit 1
) as comment
from table1 t1;
This query will make optimal use of an index on table2(user, time desc, comment) -- alas, though, I think the desc is ignored in MySQL.

How to SELECT date and time within that date?

I read few articles about this: Select max date, then max time This one seems most helpful but I do not see way to implement it.
There is five tables. I join them. I need to select only one row with highest date and highest time from first table and same from second table and join the rest on some other value. With the code I wrote I get multiple rows. It seems time selection is not right.
It might be done with subquery in subquery. I've tried something like this:
SELECT * from table1
INNER JOIN table2 ON table1.date = table2.date AND table1.gm = table2.gm
INNER JOIN table3 ON table2.gm = table3.gm ...
WHERE table3.date = :date AND table4.date = :date ...
AND table1.date IN(
SELECT MAX(table1.date) FROM table1 WHERE table1.time IN(
SELECT MAX(table1.time) FROM table1
)
)
AND table2.date IN(
SELECT MAX(table2.date) FROM table1 WHERE table2.time IN(
SELECT MAX(table2.time) FROM table2 )
)
ORDER BY table1.id
Question is:
How to get single row after joining all of this where date is highest and time is highest on that date?
Thanks!
EDIT: I am sorry for this. I forgot to say that I need max time of max date related with specific value from tables(gm columns). So that is one row(in example I gave it is table1.gm and table2.gm ... ) for each one of that .gm values which are same in every table, not just one row all together. Solutions Nick and Salim provided works but I did not solved problem.
EDIT: SOLVED! after implementing solutions by Nick I just neded to add GROUP BY cntrs_reper.gm_company_no, cntrs_reper.date.
And that's it. For every row in one table enties with highest date and time from others!! Thanks to all.
EDIT. If this can help this is full query:
SELECT cntrs_gm.gm_company_no AS company_c_g,
bns_gms.ded_bns AS ded_bns_gms,
bns_gms.no_ded_bns AS no_ded_bns_gms,
bns_gms.wag_ded_bns AS wag_ded_bns_gms,
cntrs_gm.cur_credit AS cur_credit_c_g,
cntrs_gm.cdrop AS cdrop_c_g,
cntrs_gm.total_jp AS total_jp_c_g,
cntrs_gm.games AS games_c_g,
cntrs_gm.wgames AS wgames_c_g,
cntrs_gm.doors AS doors_c_g,
cntrs_gm.power AS power_c_g,
cntrs_gm.total_in AS total_in_c_g,
cntrs_gm.total_out AS total_out_c_g,
cntrs_gm.total_acc AS total_acc_c_g,
cntrs_gm.total_bet AS total_bet_c_g,
cntrs_gm.total_win AS total_win_c_g,
cntrs_gm.total_bonus AS total_bonus_c_g,
cntrs_gm.date AS date_c_g,
cntrs_reper.gm_company_no AS company_reper,
bns_reper.ded_bns AS ded_bns_reper,
bns_reper.no_ded_bns AS no_ded_bns_reper,
bns_reper.wag_ded_bns AS wag_ded_bns_reper,
cntrs_reper.cur_credit AS cur_credit_reper,
cntrs_reper.cdrop AS cdrop_reper,
cntrs_reper.total_jp AS total_jp_reper,
cntrs_reper.games AS games_reper,
cntrs_reper.wgames AS wgames_reper,
cntrs_reper.doors AS doors_reper,
cntrs_reper.power AS power_reper,
cntrs_reper.total_in AS total_in_reper,
cntrs_reper.total_out AS total_out_reper,
cntrs_reper.total_acc AS total_acc_reper,
cntrs_reper.total_bet AS total_bet_reper,
cntrs_reper.total_win AS total_win_reper,
cntrs_reper.total_bonus AS total_bonus_reper,
cntrs_reper.date AS date_reper,
cntrs_reper.time AS time_reper,
bns_reper.time AS time_c_g,
gms_cfg.gm_no AS machine_id,
gms_cfg.denom_cin AS machine_cin
FROM bns_gms
INNER JOIN cntrs_gm
ON bns_gms.gm_company_no = cntrs_gm.gm_company_no AND bns_gms.date = cntrs_gm.date
INNER JOIN bns_reper
ON cntrs_gm.gm_company_no = bns_reper.gm_company_no
INNER JOIN cntrs_reper
ON bns_reper.gm_company_no = cntrs_reper.gm_company_no AND bns_reper.date = cntrs_reper.date
INNER JOIN gms_cfg
ON cntrs_reper.gm_company_no = gms_cfg.gm_no
WHERE bns_reper.date IN(
SELECT MAX(DATE(bns_reper.date)) FROM bns_reper WHERE bns_reper.time IN(
SELECT MAX(TIME(bns_reper.time)) FROM bns_reper
)
)
AND cntrs_reper.date IN(
SELECT MAX(DATE(cntrs_reper.date)) FROM cntrs_reper WHERE cntrs_reper.time IN(
SELECT MAX(TIME(cntrs_reper.time)) FROM cntrs_reper
)
)
ORDER BY cntrs_gm.gm_company_no
DB example
bns_gms
bns_reper
cntrs_gm
cntrs_reper
gms_cfg
The problem with your current query is that it will select all rows where table1.date is the latest date on which the highest time occurs, which may well be more than one e.g. for data such as
id date time
1 2018-03-30 18:40
2 2018-03-31 12:20
3 2018-03-31 19:20
Your WHERE clause:
table1.date IN(
SELECT MAX(table1.date) FROM table1 WHERE table1.time IN(
SELECT MAX(table1.time) FROM table1
)
will select rows with id=2 and id=3 as they both have date = '2018-03-31' which is when the maximum time occurs.
What you want to do is select the row which has the latest time on the latest date, for which you could use
table1.date = (SELECT MAX(date) FROM table1) AND
table1.time = (SELECT MAX(time) FROM table1 WHERE date = (SELECT(MAX(date) FROM table1))
By using aliasing, that can be simplified (since we already know table1.date = MAX(date) FROM table1) to
table1.date = (SELECT MAX(date) FROM table1) AND
table1.time = (SELECT MAX(time) FROM table1 AS t1 WHERE t1.date = table1.date)
I don't have MySQL but here is the general idea you can use. I don't have enough points to write a comment so I am responding as a reply. Essentially make a subquery/inline view for each table to select max of a column, then join those subqueries/inline views together.
Here is Oracle syntax. You can convert it to ANSI syntax.
select table1.column1, table2.column2,table3.column3
from
(select id1, max(column1) as column1 from table1 group by id1) as table1
(select id2, max(column2) as column2 from table2 group by id2) as table2
(select id3, max(column3) as column3 from table3 group by id3) as table3
where
table1.id1 = table2.id2
and table1.id1 = table3.id3
;

Mysql Join 3 Tables in One Query with sorted Result

I have 3 tables and want to join all in one query to show latest 10 entries by datetime.
t1: id, username
t2: id, id_t1, med_id, ga_id, au_id, re_id, text, datetime
t3: id, id_t1, pro_id, au_id, re_id, text, datetime
First I saw it would be easy with simple left join and where id, but i got double results. Then i tried inner and outer join, also group by, but the result was bad.
So my question is how can i join all without double results of the last 10 of t2 and t3?
Hard to tell what exactly you are trying to acheive, but here is a clue how it could be complemented.
SELECT TOP 10 DISTINCT T1.*
FROM T1
INNER JOIN T2 ON T1.id = T2.id_t1
INNER JOIN T3 ON T1.id = T3.id_t1
ORDER BY (CASE WHEN T2.[DateTime] > T3.[DateTime] THEN
T2.[DateTime]
ELSE
T3.[DateTime]
END) DESC
If you need to select field from T2 and T3, GROUP BY on all T1 field with aggregate on field from t2 and t3 is an option. Otherwise, linked-subquery is the way to go.
As sgeddes commented already, it's hard to know what you need, without seeing some example data from your tables. It would really help to know what the relationship between the three tables is.
One question I have, in particular, is: how are t2 and t3 related, if at all? It looks like they might not be, as each of them has its own datetime column.
Perhaps the following could do the job, but we need some more info to know for sure:
(SELECT DISTINCT t1.*, t2.id, t2.au_id, t2.re_id, t2.text, t2.`datetime`, t2.med_id, t2.ga_id, NULL AS pro_id
FROM t1
INNER JOIN t2 ON t1.id = t2.id_t1)
UNION
(SELECT DISTINCT t1.*, t3.id, t3.au_id, t3.re_id, t3.text, t3.`datetime`, NULL AS med_id, NULL AS ga_id, t3.pro_id
FROM t1
INNER JOIN t3 ON t1.id = t3.id_t1)
ORDER BY datetime DESC
LIMIT 10
The following selects the username and the datetime for the last ten posts.
SELECT username, last_ten.`datetime` AS lastpost
FROM t1
INNER JOIN (
SELECT 't2' AS tab, id, `datetime`, t2.id_t1
FROM t2
UNION ALL
SELECT 't3' AS tab, id, `datetime`, t3.id_t1
FROM t3
ORDER BY datetime DESC
LIMIT 10
) AS last_ten ON t1.id = last_ten.id_t1