SQL: query with complex subqueries - mysql

I have the following tables in my game's database:
rankedUp (image_id, user_id, created_at)
globalRank (image_id, rank )
matchups (user_id, image_id1, image_id2)
All image_ids in globalRank table are assigned a rank which is a float from 0 to 1
Assuming I have the current logged in user's "user_id" value, I'm looking for a query that will return a pair of image ids (imageid1, imageid2) such that:
imageid1 has lower rank than imageid2 but is also the next highest rank less than imageid2
matchups table doesn't have (userid,imageid1,imageid2) or (userid,imageid2,imageid1)
rankedup table doesn't have (userid,imageid1) or if it does, the createdat column is older than X hours
What I have so far for requirement 1 is this:
SELECT lowerImages.image_id AS lower_image, higherImages.image_id AS higher_image
FROM global_rank AS lowerImages, global_rank AS higherImages
WHERE lowerImages.rank < higherImages.rank
AND lowerImages.image_id = (
SELECT image_id
FROM (
SELECT image_id
FROM global_rank
WHERE rank < higherImages.rank
ORDER BY rank DESC
LIMIT 1 , 1
) AS tmp
)
but it doesnt work because I can't reference higherImages.rank in the subquery.
Does anyone know how I could satisfy all of those requirements in one query?
Thanks for your help
EDIT:
I now have this query but I don't know about the efficiency and I need to test it for correctness:
SELECT lowerImages.image_id AS lower_image,
max(higherImages.image_id) AS higher_image
FROM global_rank AS lowerImages, global_rank AS higherImages
WHERE lowerImages.rank < higherImages.rank
AND 1 NOT IN (select 1 from ranked_up where
lowerImages.image_id = ranked_up.image_id
AND ranked_up.user_id = $user_id
AND ranked_up.created_at > DATE_SUB(NOW(), INTERVAL 1 DAY))
AND 1 NOT IN (
SELECT 1 from matchups where user_id = $userId
AND lower_image_id = lowerImages.image_id
AND higher_image_id = higherImages.image_id
UNION
SELECT 1 from matchups where user_id = $user_id
AND lower_image_id = higherImages.image_id
AND higher_image_id = lowerImages.image_id
)
GROUP BY 1
the "not in" statements I'm using are all indexed so they should run fast. The efficiency problem I have is the group by and selection of the global_rank tables
This question is a revision of Pretty Complex SQL Query, which should no longer be answered.

select
(
select image_id, rank from
rankedup inner join globalRank
on rankedup.image_id = globalRank .image_id
where user_id = XXX
limit 1, 1
) as highest,
(
select image_id, rank from
rankedup inner join globalRank
on rankedup.image_id = globalRank .image_id
where user_id = XXX
limit 2, 1
) as secondhighest
I normally use SQL Server, but this i think is the translation for mysql :)

This should do the trick:
SELECT lowerImages.*, higherImages.*
FROM globalrank AS lowerImages, globalrank AS higherImages
WHERE lowerImages.rank < higherImages.rank
AND lowerImages.image_id = (
SELECT image_id
FROM (
SELECT image_id
FROM globalrank
WHERE rank < higherImages.rank
ORDER BY rank DESC
LIMIT 1,1
) AS tmp
)
AND NOT EXISTS (
SELECT * FROM matchups
WHERE user_id = $user_id
AND ((image_id1 = lowerImages.image_id AND image_id2 = higherImages.image_id)
OR (image_id2 = lowerImages.image_id AND image_id1 = higherImages.image_id))
)
AND higherImages.image_id NOT IN (
SELECT image_id FROM rankedup
WHERE created_at < DATE_ADD(NOW(), INTERVAL 1 DAY)
AND USER_ID <> $user_id
)
ORDER BY higherImages.rank
I'm assuming the PKs of matchups and rankedup include all columns in those tables. This would allow the second 2 sub-queries to utilize the PK indexes. You would probably want an ordered index on globalrank.rank to speed up the first sub-query.

Related

How to select last and last but one records

I have a table with 3 columns id, type, value like in image below.
What I'm trying to do is to make a query to get the data in this format:
type previous current
month-1 666 999
month-2 200 15
month-3 0 12
I made this query but it gets just the last value
select *
from statistics
where id in (select max(id) from statistics group by type)
order
by type
EDIT: Live example http://sqlfiddle.com/#!9/af81da/1
Thanks!
I would write this as:
select s.*,
(select s2.value
from statistics s2
where s2.type = s.type
order by id desc
limit 1, 1
) value_prev
from statistics s
where id in (select max(id) from statistics s group by type) order by type;
This should be relatively efficient with an index on statistics(type, id).
select
type,
ifnull(max(case when seq = 2 then value end),0 ) previous,
max( case when seq = 1 then value end ) current
from
(
select *, (select count(*)
from statistics s
where s.type = statistics.type
and s.id >= statistics.id) seq
from statistics ) t
where seq <= 2
group by type

SQL code: adding value to one code but shown in other code as well

could somebody help me with this, please? I was checking answers around the net but still not successful.
I have two codes, code #1:
SELECT subject_note,ticket_id,created_time,status,
UPPER(SUBSTRING(datacenter,1,3)) region
FROM sort_ticket WHERE ticket_type = 1 AND status =0 AND team_type = 1 AND MONTH(FROM_UNIXTIME(closed_date)) = MONTH(NOW())
and YEAR(FROM_UNIXTIME(closed_date)) = YEAR(NOW())
AND
(
-- ASH
owner_id = 812400897
or owner_id = 1392249056
or owner_id = 739243661
or owner_id = 100002941128738
or owner_id = 619251675
or owner_id = 502392893
)
and code #2:
SELECT
subject_note,
cyborg_verify_tries,
ticket_id,
closed_date,
created_time,
status,
UPPER(SUBSTRING(datacenter,1,3)) region
FROM sort_ticket
WHERE ticket_type = 1
AND status =0
AND team_type = 1
and (FROM_UNIXTIME(closed_date)) >= DATE_SUB(now(), INTERVAL 6 MONTH)
AND
(
-- ASH
owner_id = 812400897
or owner_id = 1392249056
or owner_id = 739243661
or owner_id = 100002941128738
or owner_id = 619251675
or owner_id = 502392893
)
Both of these codes creating table and giving me the results what is good.
Problem what I have in here is I have to add manually every NEW "owner_id" into each code.
Is there any way how I could add NEW "owner_id" only into any code and second would be updated automatically? Both info are taken from the same table "sort_ticket".
Thank you for all the help.
You can use an extra table, where you put in the user id's and replace the fixed values with a select on this data:
SELECT
subject_note,
cyborg_verify_tries,
ticket_id,
closed_date,
created_time,
status,
UPPER(SUBSTRING(datacenter,1,3)) region
FROM sort_ticket
WHERE ticket_type = 1
AND status =0
AND team_type = 1
and (FROM_UNIXTIME(closed_date)) >= DATE_SUB(now(), INTERVAL 6 MONTH)
AND
(
-- ASH
select owner_id from newtable
)
same in the other select
You can use an additional table or an additonal view. Use a join to the new table/view in your queries and add new owner_id with an insert in the table or an UNION in the view.
To solve your proble with a view you can do this:
CREATE VIEW v_sort_ticket_owner AS
SELECT 812400897 as owner_id
UNION
SELECT 1392249056 as owner_id
UNION
SELECT 739243661 as owner_id
UNION
SELECT 100002941128738 as owner_id
UNION
SELECT 619251675 as owner_id
UNION
SELECT 502392893 as owner_id
--UNION
--SELECT newnumber as ownder_id
SELECT subject_note,ticket_id,created_time,status,
UPPER(SUBSTRING(datacenter,1,3)) region
FROM sort_ticket st
JOIN v_sort_ticket_owner sto
ON st.owner_id = sto.owner_id
WHERE st.ticket_type = 1 AND st.status =0 AND st.team_type = 1 AND
MONTH(FROM_UNIXTIME(st.closed_date)) = MONTH(NOW())
and YEAR(FROM_UNIXTIME(st.closed_date)) = YEAR(NOW())

mysql query takes more than 1 day

mysql query takes more than 1 day, how to speet it up :
update posts set category_id = (
SELECT keyword_id
FROM post_keywords
where keyword_id IN (
SELECT keyword_id
FROM post_keywords
where id_post = posts.id_post)
group by keyword_id
order by count(keyword_id) DESC
limit 0,1
);
any solution ?
It's not entirely the same thing, yet the outcome will still be what you want it to be I think. I'm mostly curious to how (IF!?) this might affect the performance.
UPDATE posts
SET category_id = (
SELECT pkw.keyword_id
FROM post_keywords pkw
JOIN (
SELECT keyword_id, cnt = COUNT(*)
FROM post_keywords
GROUP BY keyword_id ) cnts
ON cnts.keyword_id = pkw.keyword_id
WHERE pkw.id_post = posts.id_post
ORDER BY cnt DESC
LIMIT 0,1
)

MySQL: SELECT query, but then delete records from that query where columns equal

This is my initial query:
SELECT bid_tag.*
FROM bid_tag join
(select serial_number, count(*) as cnt
from bid_tag where user_id = 0
group by serial_number
) tsum
on tsum.serial_number = bid_tag.serial_number and cnt > 1
order by bid_tag.serial_number
LIMIT 0, 21000;
Now from those results, I need to SELECT all where tag_design = 0 AND tag_size = 0 and then DELETE those records from the database.
I just don't know how to run a query on the results of an initial query.
Just replace SELECT with DELETE and it will delete the rows that would have been selected.
DELETE bid_tag.*
FROM bid_tag join
(select serial_number, count(*) as cnt
from bid_tag where user_id = 0
group by serial_number
) tsum
on tsum.serial_number = bid_tag.serial_number and cnt > 1
WHERE tag_design = 0 AND tag_size = 0
order by bid_tag.serial_number
LIMIT 0, 21000;
use an EXISTS term in your where clause:
DELETE
FROM bid_tag btd
WHERE EXISTS (
SELECT 1
FROM (
SELECT bid_tag.*
FROM bid_tag bts
JOIN (
SELECT serial_number, count(*) as cnt
FROM bid_tag btj
WHERE btj.user_id = 0
GROUP BY btj.serial_number
) tsum
ON ( tsum.serial_number = bts.serial_number
AND tsum.cnt > 1
)
WHERE bts.tag_design = 0
AND bts.tag_size = 0
ORDER BY bts.serial_number
LIMIT 0
, 21000
) rs_base
WHERE rs_base.id = btd.id -- PK column
)
;
the subquery in the EXISTS term can be nested further to contain another query on the result set of the original one. just make sure that you always select the primary key of the table on which the deletion is to be performed.
note that you probably don't want to restrict yourself to a part of your result set in a delete operation so check whether you need the limiting to the top 21000 results - if you dont, drop the 'ORDER BY' and 'LIMIT' clauses.

mySQL trouble building query with multiple limits

I Have this query which works with the single limit imposed at the end.
select distinct
loc.mID,
loc.city,
loc.state,
loc.zip,
loc.country,
loc.latitude,
loc.longitude,
baseInfo.firstname,
baseInfo.lastname,
baseInfo.profileimg,
baseInfo.facebookID,
(((acos(sin(('37.816876'*pi()/180)) * sin((`latitude`*pi()/180))+cos(('37.816876'*pi()/180)) * cos((`latitude`*pi()/180)) * cos((('-121.285410' - `longitude`)*pi()/180))))*180/pi())*60*1.1515) AS `distance`,
teams.teamName,
teams.leagueType,
teams.teamType,
teams.subcat
FROM memb_geo_locations loc
left join memb_friends friends on (friends.mID = loc.mID or friends.friendID = loc.mID) and (friends.mID = '100018' or friends.friendID = '100018')
join memb_baseInfo baseInfo on baseInfo.mID = loc.mID
join memb_teams teams on teams.mID = loc.mID
where
loc.primaryAddress = '1'
and ((friends.mID is null or friends.friendID is null)
or (friends.isactive = 2))
and (
teams.teamName like '%Anaheim Ducks%'
or teams.teamName like '%San Jose Sharks%'
or teams.teamName like '%New England Patriots%'
or teams.teamName like '%New York Yankees%'
or teams.teamName like '%Orlando Magic%'
)
and loc.mID != 100018
having `distance` < 50
order by baseInfo.firstname
asc limit 30
However I want my results to be limited by the teamName to 3 results max per, And I have tried stuff to the extent of
select distinct
loc.mID,
loc.city,
loc.state,
loc.zip,
loc.country,
loc.latitude,
loc.longitude,
baseInfo.firstname,
baseInfo.lastname,
baseInfo.profileimg,
baseInfo.facebookID,
(((acos(sin(('37.816876'*pi()/180)) * sin((`latitude`*pi()/180))+cos(('37.816876'*pi()/180)) * cos((`latitude`*pi()/180)) * cos((('-121.285410' - `longitude`)*pi()/180))))*180/pi())*60*1.1515) AS `distance`,
teams.teamName,
teams.leagueType,
teams.teamType,
teams.subcat
FROM memb_geo_locations loc
left join memb_friends friends on (friends.mID = loc.mID or friends.friendID = loc.mID) and (friends.mID = '100018' or friends.friendID = '100018')
join memb_baseInfo baseInfo on baseInfo.mID = loc.mID
join memb_teams teams on teams.mID = loc.mID
where
loc.primaryAddress = '1'
and ((friends.mID is null or friends.friendID is null)
or (friends.isactive = 2))
and (
(select * from memb_teams where teamName like '%Buffalo Bills%' limit 2),
(select * from memb_teams where teamName like '%San Jose Sharks%' limit 2),
(select * from memb_teams where teamName like '%New England Patriots%' limit 2)
)
and loc.mID != 100018
having `distance` < 150
order by baseInfo.firstname
asc limit 30
With no success, usually just syntax errors.. or Operand Should 1 Column(s) so I am reaching out here hopefully someone can give me some idea how to refine my query a bit so I can limit the results to 3 per teamName.. rather than having staggered results where I could have 20 of one and 4 of another 2 of another and 1 and 1 (which is not desired). 3 or less per team is desired, Just don't know how. Ideas, that don't involve tackling a huge data set from the query and looping over it via server side code to output results I desire?
IN MSSQL I use ROW_NUMBER function, and it would be something like this:
SELECT * FROM dbo.MyTable WHERE recno
IN (SELECT recno FROM (SELECT Teamname, ROW_NUMBER() OVER (PARTITION BY Teamname ORDER BY recno DESC) AS intRow FROM dbo.MyTable) AS T
WHERE intRow IN (1,2,3))
recno=your unique record number
Basically your Subquery selects the top 3 records, adding a new "ROW NUMBER" column.
The Top Query selects all the records with Rownumber between 1 to 3.
I know there is no ROW_NUMBER() native function in MYSQL, so you could use this instead:
MySQL - Get row number on select