Mysql ABS function unexplained bug in code - mysql

Consider the following query:
SELECT p . *
FROM multiple_picks p
WHERE p.event_id = '3303'
AND p.pick = 'Highlanders'
AND ABS( p.score - '8' ) = (
SELECT MIN( ABS( p2.score -1 ) )
FROM multiple_picks p2
WHERE p2.pick = p.pick
AND p2.event_id = p.event_id )
The query above should return the member(s) who selected the CORRECT TEAM AND the MEMBER(S) who was CLOSEST to Selecting the score. If it is a tie - more than one member should be returned!
When I run the above query on the following table, named multiple_picks:
I get the following result back:
MY PROBLEM
The result returned is incorrect as you can clearly see!
Since the winning team was the Highlanders with a score by 8 (as seen in the query)
The correct result which should have been returned is row2 (the member who picked the Highlanders by 10!)
Any advice as to why I am getting this incorrect result, or what I am doing wrong will be greatly appreciated! Been stuck on this for days now!
SQL FIDDLE

The problem here is that p.score - (const) is compared to p2.score - 1 which is not consistent.
Here's the idea (If I understood the question correctly).
Assume you have variables (or something like that):
T = 'Highlanders'
S = 8
This means you need to find the entry which has p.pick=T and for which the p.score is the closest to S.
Here's what you do (replaced the hardcoded values with the variables above to not distract from the essence of the question) :
SELECT p . *
FROM multiple_picks p
WHERE p.event_id = '3303'
AND p.pick = T
AND ABS(p.score - S) = (
SELECT MIN(ABS(p2.score - S))
FROM multiple_picks p2
WHERE p2.pick = p.pick
AND p2.event_id = p.event_id
)
Updated SQL Fiddle

Related

Chain of SQL subqueries within large query of JOINS

I am trying to structure a SQL query with complex nested select operation!
My original SQL query successfully JOINS around 30 tables, the data are retrieved as wanted! However every fetched record in the 30 tables
has many records in another table called (Comments)! What I want to do is to atribute every record in the (Comments table) to its record in the other
30 tables by IDs and retrieve them all together in one query. Yet this is not the only challenge, some of the 30 tables have in addition to the records in
(Comments table) more records in another table called (extras), so i am looking for additional subquery within the main subquery within
a LEFT JOIN inside the outter main query.
To make the idea more clear; without subquery the script will be as following:
$query = $mysqli->query("
SELECT
parent1.parent1_id,
parent1.child1_id,
parent1.child2_id,
parent1.child3_id,
parent2.parent2_id,
parent2.child1_id,
parent2.child2_id,
parent2.child3_id,
child1.child1_id,
child1.child1_content,
child2.child2_id,
child2.child2_content,
child3.child3_id,
child3.child3_content
FROM
parent1
LEFT JOIN child1
ON child1.child1_id = parent1.child1_id
LEFT JOIN child2
ON child2.child2_id = parent1.child2_id
LEFT JOIN child3
ON child3.child3_id = parent1.child3_id
LEFT JOIN followers
ON parent1.user_id = followers.followed_id
AND parent1.parent1_timestamp > followers.followed_timestamp
AND parent1.parent1_id NOT IN (SELECT removed.isub_rmv FROM removed)
AND parent1.parent1_hide = false
WHERE
followers.follower_id = {$_SESSION['info']}
{$portname_clause}
ORDER BY
parent1.parent1_timestamp DESC
LIMIT
{$postnumbers}
OFFSET
{$offset}
")
// Now fetching and looping through the retrieved data
while($row = $query->fetch_assoc()){
echo $row['child1_content'];
$subquery1 = $mysqli->query("SELECT extras.child1_id,
extras.extrasContent FROM extras WHERE extras.child1_id =
{$row['child1_id']}");
while($row1 = $subquery1->fetch_assoc()){
echo $row1['extrasContent'];
}
echo $row['child2_content'];
$subquery2 = $mysqli->query("SELECT extras.child2_id,
extras.extrasContent FROM extras WHERE extras.child2_id =
{$row['child2_id']}");
while($row2 = $subquery2->fetch_assoc()){
echo $row2['extrasContent'];
}
echo $row['child3_content'];
$subquery3 = $mysqli->query("SELECT extras.child3_id,
extras.extrasContent FROM extras WHERE extras.child3_id =
{$row['child3_id']}");
while($row3 = $subquery3->fetch_assoc()){
echo $row3['extrasContent'];
// Here i need to run additional query inside the subquery 3 to retrieve the (Comments table) data beside (extras table)
$subquery4 = $mysqli->query("SELECT comments.comment_id, comments.comment FROM comments WHERE comments.child3_id = {$row['child3_id']} OR comments.child3_id = {$row3['child3_id']}");
while($row4 = $subquery4->fetch_assoc()){
echo $row4['comment'];
}
}
} // No sane person would make such code
Because the code above would be totally rediclious i searched for a better way to carry it out, and thats where i came across the subquery
concept, but i do not know anything about subqueries, and shortly after i studied it i came up with this messy code, check it below!
I am not posting the origianl code here because it is too long, i am including a virtual example of the tables i want to apply the
query on in order to demonstrate the process.
SELECT
parent1.parent1_id,
parent1.child1_id,
parent1.child2_id,
parent1.child3_id,
parent2.parent2_id,
parent2.child1_id,
parent2.child2_id,
parent2.child3_id
FROM
parent1
LEFT JOIN
( SELECT
child1.child1_id,
child1.child1_content
FROM
child1
WHERE
child1.child1_id = parent1.child1_id ) child1
( SELECT extras.extrasID, extras.extrasContent
FROM
extras
WHERE
extras.child1_id = child1.child1_id )
ON parent1.child1_id = child1.child1_id
LEFT JOIN child2
( SELECT
child2.child2_id,
child2.child2_content
FROM
child2
WHERE
child2.child2_id = parent1.child2_id )
( SELECT
extras.extrasID,
extras.extrasContent
FROM
extras
WHERE
extras.child2_id = child2.child2_id )
ON parent1.child2_id = child2.child2_id
LEFT JOIN child3
( SELECT
child3.child3_id,
child3.child3_content
FROM
child3
WHERE
child3.child3_id = parent1.child3_id )
( SELECT
extras.extrasID,
extras.extrasContent
FROM
( SELECT
comments.comment_id,
comments.comment
FROM
comments
WHERE
comments.child3_id = extras.child3_id ) extras
JOIN child3
ON extras.child3_id = child3.child3_id )
ON parent1.child3_id = child3.child3_id
LEFT JOIN followers
ON parent1.user_id = followers.followed_id
AND parent1.parent1_timestamp > followers.follower_timestamp
AND parent1.parent1_id NOT IN (SELECT removed.isub_rmv FROM removed)
AND parent1.parent1_hide = false
WHERE
followers.follower_id = {$_SESSION['info']}
{$portname_clause}
ORDER BY
parent1.parent1_timestamp DESC
LIMIT
{$postnumbers}
OFFSET
{$offset} // <-- Sorry for the bad code formatting!
I am using MySql 5.6.37
I did not get the hang of the subquery concept yet, frankly i got lost and confused as i was studying it and for another reason too mentioned in the note below.
Note: I apologize in advance that i might not be on instant reply because where i live there is no electrecity or ADSL or phones and my
USB modem hardly gets signal, i have only two hours of averege three hours a day of electericity generated by a desil generator. i recharge
my laptop and check internet and the remaining one-two hours are for other life stuff.
I know the joke is on me as i am developing a web project without electricity or permanent internet. BUT LIFE DOES NOT GIVE EVERYTHING! lol.
This is how i solved the problem!
SELECT
parent1.parent1_id,
parent1.child1_id,
child1.child1_id,
child1.child1_content,
comments.comment_id,
comments.comment,
comments.child1_id
FROM parent1 LEFT JOIN
(
SELECT comments.comment_id, comments.comment, comments.child1_id
FROM
(
SELECT comments.comment_id,comments. comment, comments.child1_id
FROM comments
) comments JOIN child1
ON comments.child1_id = child1.child1_id
) comments
ON child1.child1_id = comments.
It needs some aliases and then it's good to go.

Convert time "28:45" to "4:45" MySQL

I'm looking for a way to order my results based on the actual time. In my table yo can see values like:
1,23:45
2,9:45
3,27:43
When I do a query I would like to know how to order them based on their actual 24 hour time.
Ex:
3,3:43
2,9:45
1,23:45
Notice how it changes 27:43 to 3:43, and creates the order.
Where I am using it, in this query:
SELECT *,COALESCE(ADDTIME(s.`departure_time`,SEC_TO_TIME(rt.delay)),s.`departure_time`) as `rt_time` FROM `stop_times` s INNER JOIN `trips` t ON s.`trip_id` = t.`trip_id` INNER JOIN `stops` st ON st.`stop_id` = s.`stop_id` INNER JOIN `routes` r ON r.`route_id` = t.`route_id` LEFT JOIN `rt_trips` rt ON t.`trip_id` = rt.`trip_id` where (s.`stop_id` = 'CB900') and ( ( s.`departure_time` >= '00:50' and s.`departure_time` <= '05:50') OR ( s.`departure_time` >= '24:50' and s.`departure_time` <= '29:50') ) and (s.`pickup_type` = '0') and (t.`service_id` IN ('removed to make it easier')) HAVING (`rt_time` BETWEEN '01:50' and '05:50' ) ) OR ( `rt_time` BETWEEN '25:50' and '29:50' ) ORDER BY `order` ASC
Explanation:
Information is a transit schedule, that may go forward onto the next day which may be a saturday. So, times may become 25:50, where that means 1:50 the next day.
Thanks
Cyrus
Hmmm, if you just want to get a value between 0 and 24 hours, then I would do:
select concat(mod(substring_index(time_column, ':', 1) + 0, 24), ':',
substring_index(time_column, ':', -1)
)
Try this function on the time_column
concat(mod(substr(time_column,1,INSTR(time_column, ':')-1),24)
,substr(time_column,INSTR(time_column, ':'),3)
)
You might need to cast date to string to integer, do the maths, and again cast it to time. But the fiddle version seems to work properly on varchar to integer conversion. Check this
http://sqlfiddle.com/#!9/ff60f9/1

MySQL: Conditional Join Not Showing All Data For Calculating Distance Between Lat/Longs

Problem
I am performing a conditional join using an if statement and it is failing to return all of the records I would ideally expect it to return.
Users on my app input a location via an autocomplete input form and select a distance in miles. The hardcoded lat/long coordinates in the query below are the lat/long coords of the location the user inputted in the form (therefore it's dynamic) and the distance is the distance in miles the user inputted (also dynamic).
The query should first check to see if an at_resources has a "place served" associated to their resource id within the specified miles. If a resource id does not have any location specified in the at_places_served table it should use the zipcode_name column in the at_resources table as a fallback. Also note zipcode_name can be NULL.
In particular, in the event a resource does not have a places served record it will use their resource table record to calculate the distance.
Below is my code and screenshot of what this particular query returns.
Please let me know if you would like me to provide you with sample MySQL data so you know what the tables look like or explain anything further.
Thank you and regards.
Code
SELECT resource.id id, resource.resource_name_line_1, resource.resource_name_line_2, resource.resource_handle, zips.zip_display_name, zips.lat, zips.long,
MIN((3959 * acos(cos(radians(42.3122)) * cos(radians(zips.lat))
* cos(radians(zips.long) - radians(-71.1947)) + sin(radians(42.3122))
* sin(radians(zips.lat))))) AS distance
FROM at_resources resource
JOIN at_resources_places_served places_served
ON places_served.resource_id = resource.id
JOIN at_zip_mapping zip_mapping
ON IF(places_served.resource_id IS NOT NULL, zip_mapping.zip_name_id = places_served.zipcode_name_id AND places_served.active = 1, zip_mapping.zip_name_id = resource.zipcode_name AND resource.active = 1)
JOIN at_zips zips
ON zips.id = zip_mapping.zip_id
GROUP BY resource.id
HAVING distance < 25
ORDER BY distance, resource.resource_name_line_1 ASC
Here is a SQL Fiddle: http://sqlfiddle.com/#!9/c17155/1
Query
Try this:
SELECT
resource.id id,
resource.resource_name_line_1,
resource.resource_name_line_2,
resource.resource_handle,
zips.zip_display_name,
zips.lat, zips.long,
MIN((3959 * acos(cos(radians(42.3122)) * cos(radians(zips.lat))
* cos(radians(zips.long) - radians(-71.1947)) +
sin(radians(42.3122))
* sin(radians(zips.lat))))) AS distance
FROM at_resources resource
LEFT JOIN at_resources_places_served places_served
ON places_served.resource_id = resource.id
LEFT JOIN at_zip_mapping zip_mapping_by_id
ON
places_served.resource_id IS NOT NULL AND
zip_mapping_by_id.zip_name_id = places_served.zipcode_name_id AND
places_served.active = 1
LEFT JOIN at_zip_mapping zip_mapping_by_zipcode
ON
places_served.resource_id IS NULL AND
zip_mapping_by_zipcode.zip_name_id = resource.zipcode_name AND
resource.active = 1
JOIN at_zips zips
ON places_served.resource_id IS NOT NULL AND zips.id = zip_mapping_by_id.zip_id
JOIN at_zips zips_by_zipcode
ON places_served.resource_id IS NULL AND
zips_by_zipcode.id = zip_mapping_by_zipcode.zip_id
GROUP BY resource.id
HAVING distance < 25
ORDER BY distance, resource.resource_name_line_1 ASC
Update:
I add more conditional join with at_zips if places_served.resource_id IS NOT NULL and if they are not.
What you are after are some Left Joins: This will add nulls to row if it doesn't have something to join to instead of omitting it:
SELECT resource.id id ,
resource.resource_name_line_1 ,
resource.resource_name_line_2 ,
resource.resource_handle ,
zips.zip_display_name ,
zips.lat ,
zips.long ,
MIN(( 3959 * ACOS(COS(RADIANS(42.3122)) * COS(RADIANS(zips.lat))
* COS(RADIANS(zips.long) - RADIANS(-71.1947))
+ SIN(RADIANS(42.3122)) * SIN(RADIANS(zips.lat))) )) AS distance
FROM at_resources resource
LEFT JOIN at_resources_places_served places_served ON places_served.resource_id = resource.id
LEFT JOIN at_zip_mapping zip_mapping ON IF(places_served.resource_id IS NOT NULL, zip_mapping.zip_name_id = places_served.zipcode_name_id AND places_served.active = 1, zip_mapping.zip_name_id = resource.zipcode_name AND resource.active = 1)
LEFT JOIN at_zips zips ON zips.id = zip_mapping.zip_id
GROUP BY resource.id
HAVING distance < 25
ORDER BY distance
, resource.resource_name_line_1 ASC
Without test data I cant really test if your ON statements look ok, the look good at a glance, and they seem to be doing the job for you, so fingers crossed! If not, try setting up a http://sqlfiddle.com/ for us and we can play with it a bit better

IF condition in mysql

I have a contact table I wish to query when a certain condition exists. I tried the query below but am getting a syntax error.
SELECT *
FROM contact_details
WHERE contactDeleted` =0
AND IF ( contactVisibility = "private"
, SELECT * FROM contact_details
WHERE contactUserId = 1
, IF( contactVisibility = "group"
, SELECT * FROM contact_details
WHERE contactGroup = 3
)
)
If I'm understanding your question correctly (which is difficult with the lack of info you've provided. Sample datasets and expected outcomes are typically helpful), then I don't believe you need IFs at all for what you want. The following will return contacts that are not deleted and who either have (visibility = "private" and userId = 1) OR (visibility = "group" and group = 3)
SELECT *
FROM contact_details
WHERE contactDeleted = 0
AND (
(contactVisibility = "public")
OR
(contactVisibility = "private" AND contactUserId = 1)
OR
(contactVisibility = "group" AND contactGroup = 3)
)
I am assuming you want to use the IF() function and not the statement which is for stored functions..
Refer to this link for more information on that.
Notice that you have put 2 select statements in there, where the custom return values are supposed to be. So you are returning a SELECT *... now notice that in your upper level sql statement you have an AND.. so you basically writing AND SELECT *.. which will give you the syntax error.
Try using .. AND x IN (SELECT *) .. to find if x is in the returned values.
Let me also list this link to make use of an existing and well written answer which may also applicable to your question.

Simplify sql query to obtain one line per id

I have a multi-table SQL query.
My need is: The query should I generate a single line by 'etablissement_id' ... and all information that I want to be back in the same query.
The problem is that this query is currently on a table where "establishment" may have "multiple photos" and suddenly, my query I currently generates several lines for the same id...
I want the following statement - LEFT JOINetablissementContenuMultimediaON etablissement.etablissement_id = etablissementContenuMultimedia.etablissementContenuMultimedia_etablissementId - only a single multimedia content is displayed. Is it possible to do this in the query below?
Here is the generated query.
SELECT DISTINCT `etablissement`. * , `etablissementContenuMultimedia`. * , `misEnAvant`. * , `quartier`. *
FROM `etablissement`
LEFT JOIN `etablissementContenuMultimedia` ON etablissement.etablissement_id = etablissementContenuMultimedia.etablissementContenuMultimedia_etablissementId
LEFT JOIN `misEnAvant` ON misEnAvant.misEnAvant_etablissementId = etablissement.etablissement_id
LEFT JOIN `quartier` ON quartier_id = etablissement_quartierId
WHERE (
misEnAvant_typeMisEnAvantId =1
AND (
misEnAvant_dateDebut <= CURRENT_DATE
AND CURRENT_DATE <= misEnAvant_dateFin
)
)
AND (
etablissement_isActive =1
)
ORDER BY `etablissement`.`etablissement_id` ASC
LIMIT 0 , 30
Here is the code used ZF
public function find (){
$db = Zend_Db_Table::getDefaultAdapter();
$oSelect = $db->select();
$oSelect->distinct()
->from('etablissement')
->joinLeft('etablissementContenuMultimedia', 'etablissement.etablissement_id = etablissementContenuMultimedia.etablissementContenuMultimedia_etablissementId')
->joinLeft('misEnAvant', 'misEnAvant.misEnAvant_etablissementId = etablissement.etablissement_id')
->joinLeft('quartier', 'quartier_id = etablissement_quartierId ')
->where ('misEnAvant_typeMisEnAvantId = 1 AND (misEnAvant_dateDebut <= CURRENT_DATE AND CURRENT_DATE <= misEnAvant_dateFin) ')
->where ('etablissement_isActive = 1')
->order(new Zend_Db_Expr('RAND()'));
$zSql = $oSelect->__toString();
if(isset($_GET['debug']) AND $_GET['debug'] == 1)
echo $zSql ;
//die();
$oResultEtablissement = $db->fetchAll($oSelect);
return $oResultEtablissement ;
}
Can you help me?
Sincerely,
If you are looking to have only one of the media displayed out of many regardless of which it may be then you can just add a limit to the query? After that you can tweak the query for ASCending or DESCending perhaps?
Is this query supposed to have images (or image as it were) for one establishment, or one image each for each active establishment? I see you have a limit 0,30 which means you're likely paginating....
If the result you want is a search for only one establishment, and the first image it comes to would work fine .. just use "limit 1" and you'll only get one result.
I took the time to redo the whole model of the database ... and now it works. There was no solution for a system as flawed