First record not shown UNION ALL - php

I have the following problem.
I have a MySQL-query with UNION ALL:
$zoek = trim($_POST['zoekkey']);
$zoekstring = "%" . $zoek . "%";
$stmtsk = $user_home->runQuery(
"(SELECT city_zip, city_name, tp_status, tp_city as city, themepark_id as id, tp_logo as logo, tp_name as name, tp_address as address, 'Uitstap' as categorie FROM themepark "
. "INNER JOIN city ON themepark.tp_city=city.city_id "
. "WHERE (tp_name LIKE :search OR tp_address LIKE :search) AND tp_status = 1) "
. "UNION ALL "
. "(SELECT city_zip, city_name, hot_status, hot_city as city, hotel_id as id, hot_logo as logo, hot_name as name, hot_address as address, 'Overnachten' as categorie FROM hotel "
. "INNER JOIN city ON hotel.hot_city=city.city_id "
. "WHERE (hot_address LIKE :search OR hot_name LIKE :search) AND hot_status = 1) "
. "UNION ALL "
. "(SELECT city_zip, city_name, rest_status, rest_city as city, restaurant_id as id, rest_logo as logo, rest_name_".$_SESSION['lang']." as name, rest_address as address, 'Restaurant' as categorie FROM restaurant "
. "INNER JOIN city ON restaurant.rest_city=city.city_id "
. "WHERE (rest_address LIKE :search OR rest_name_".$_SESSION['lang']." LIKE :search) AND rest_status = 1) "
. "UNION ALL "
. "(SELECT city_zip, city_name, sbs_status, sbs_city as city, sbs_id as id, sbs_logo as logo, sbs_name as name, sbs_address as address, 'Detailhandel' as categorie FROM sbs " //detailhandel
. "INNER JOIN city ON sbs.sbs_city=city.city_id "
. "WHERE (sbs_address LIKE :search OR sbs_name LIKE :search) AND sbs_status = 1 AND sbs_categorie = 'detail') "
. "UNION ALL "
. "(SELECT city_zip, city_name, sbs_status, sbs_city as city, sbs_id as id, sbs_logo as logo, sbs_name as name, sbs_address as address, 'Horeca' as categorie FROM sbs " //horeca
. "INNER JOIN city ON sbs.sbs_city=city.city_id "
. "WHERE (sbs_address LIKE :search OR sbs_name LIKE :search) AND sbs_status = 1 AND sbs_categorie = 'horeca')
`ORDER BY categorie,name`"
);
$stmtsk->bindParam(":search",$zoekstring);
$stmtsk->execute();
$rowsk = $stmtsk->fetch(PDO::FETCH_ASSOC);
The strange thing is, the first row off the result is not coming up.
All the others are OK.
When I run the same query in PHPMyAdmin it is working fine...
I really don't understand why it isn't showing it.
Thanks in advance!

Very unlikely it's a problem with the query. But we can't entirely rule out an issue with the LIKE comparisons and a characterset difference. And we can't rule out issues with the values being supplied for the bind parameters,
We're just guessing. But the most likely explanation for the observed behavior is that the code is fetching the first row, and then doing nothing with it, and then fetching the next row. We could reproduce the reported behavior with a code pattern like this:
$sth->execute();
$row = $sth->fetch();
// do nothing with the row we just fetched
while( $row = $sth->fetch() ) {
// fetch another row and output it
}
This pattern would explain why we don't "get" the first row. (We actually get the row, we just ignore it, and fetch the next row.
Other thoughts:
Without an ORDER BY clause, the database can return rows in any order. So what is the "first" row with one execution could be the third row, or last row, on subsequent executions.
If the issue was with particular rows (values) not being returned, other than just the reported "missing first row", that would likely be due to conditions in the WHERE clause, how the query is evaluated. But that issue could affect more than just the one "first" row.

Related

Need to add filters to a complex query

i am trying to write a query which will help me recover class, batch, and institution details from db
so far i have managed to make the following work:
SELECT
gallery.path_url AS insimage,
r3.*
FROM
gallery
INNER JOIN (
SELECT
institution.id AS insid,
institution.ProfileImageID,
institution.`name` AS insname,
institution.Locality AS inslocality,
institution.RegistrationFee AS ins_reg_fee,
institution.IsTrail AS ins_is_trail,
institution.LatLong AS latlong,
r2., activity.`name` AS activityname
FROM
institution
INNER JOIN (
SELECT
class., count() AS batch_count,
r1.
FROM
class
INNER JOIN (
SELECT
batch.ID AS batch_id,
batch.ClassID AS class_id,
batch.LevelID AS level_id,
batch.agegroupID AS agegroup_id,
count(*) AS num_batches,
min(Price) AS min_price,
max(Price) AS max_price
FROM
batch,
batchstarttimes
WHERE
batch.ID = batchstarttimes.BatchID
AND batchstarttimes.StartTime BETWEEN '" . $sttime . "'
AND '" . $endtime . "'
GROUP BY
batch.ID
) AS r1
WHERE
r1.class_id = class.ID
GROUP BY
r1.class_id
) AS r2 ON r2.institutionid = institution.id
INNER JOIN activity ON activity.id = r2.activityid
WHERE
activity.`name` LIKE '%" . $searchterm . "%'
AND institution.Locality LIKE '%" . $locality . "%'
AND institution.StatusID = 1
AND level_id = 1
AND agegroup_id = 5
) AS r3 ON r3.ProfileImageID = gallery.ID
I am stuck when i try to add two more filters batch.LevelID and batch.agegroupID
any way i can get help or someone point me to a sql visualizr tool will be helpful.
In your WHERE clause, replace:
AND level_id = 1
AND agegroup_id = 5
with:
AND batch.LevelID = CASE WHEN " . $levelID . " = 0 THEN batch.LevelID ELSE " . $levelID . " END
AND batch.agegroupID = CASE WHEN " . $ageGroupID . " = 0 THEN batch.agegroupID ELSE " . $ageGroupID . " END
The point is to compare batch.LevelID and batch.agegroupID against themselves if you want to ignore them for the filtering (this way they'll always be true) or compare them against $levelID and $ageGroupID values for specific rows.
Also, it's not recommended to use the variables directly in the query, as it's vulnerable to SQL injections attacks. Read about prepared statements here.

How to create such query?

I have four tables:
tournaments (id, name, slots, price, gameId, platformId)
games (id, name)
platforms (id, name)
participations (id, tournamentId, playerId)
I want to get tournament's game name, platform name, slots, price, reservedSlots (participations count), information whether some player (his id is provided by php) participate in this tournament (bool/true) and conditions are:
- gameId must be in specified array provided by php
- platformId must be in specified array provided by php
I have created something like this but it doesn't work correctly:
php:
$platformsList = "'". implode("', '", $platforms) ."'"; //
$gamesList = "'". implode("', '", $games). "'";
mysql:
SELECT
t. NAME AS tournamentName,
t.id,
t.slots,
p. NAME,
g. NAME AS gameName,
t.price
FROM
tournaments AS t,
platforms AS p,
games AS g,
participations AS part
WHERE
t.PlatformId = p.Id
AND t.GameId = g.Id
AND t.Slots - (
SELECT
count(*)
FROM
participations
WHERE
TournamentId = t.id
) > 0
AND part.TournamentId = t.Id
AND t.PlatformId IN ($platformsList)
AND t.GameId IN ($gamesList)
I will not dwelve into handling your post and get values, I will assume that everything is all right:
$possibleGameIDs = getPossibleGameIDs(); //function will return the array you need for possible game id values. Inside your function make sure that the id values are really numeric
$possiblePlatformIDs = getPossiblePlatformIDs(); //function will return the array you need for possible platform id values. Inside your function make sure that the id values are really numeric
$playerId = getPlayerId(); //function returns the player id and makes sure that it is really a number
$sql = "select games.name, platforms.name, tournaments.slots, tournaments.price, ".
"(select count(*) from participations where participations.tournamentId = tournaments.tournamentId) as reservedSlots, ".
"(select count(*) > 0 from participations where participations.tournamentId = tournaments.tournamentId and playerId = ".$playerId.") as isParticipating ".
"from tournamens ".
"join games ".
"on tournaments.gameId = games.id ".
"join platforms ".
"on tournaments.platformId = platforms.id ".
"where games.id in (".implode(",", $possibleGameIDs).") and ".
" platforms.id in (".implode(",", $possiblePlatformIDs).") and ".
" tournaments.slots > 0"
Code was not tested, so please, let me know if you experience any problems using it. Naturally you need to run it, but as you did not specify what do you use to run the query, I did not allocate time to deal with technical details of running it. Beware SQL injections though.

PHP/MYSQL SEARCH QUERY RETURNS ERROR: Subquery returns more than 1 row

please I am totally new to mysql, my problem is:
I have a table called 'cong' which has the following columns(id, sort_code, pin, name, state, lga, zip, address, min, min_photo, sec, min_phone, sec_phone) which contains all congregations.
The columns (state, lga) contains the id from the tables 'states' and 'local_govt'.
The 'states' table has the following columns (id, country_id, name), and the 'local_govt' table has the following columns (id, country_id, state_id, name).
I want to carry out a search on the 'cong' table which should search through the 'state' and 'local_govt' tables for matches, below is the search function I wrote:
<?php
function find_cong($term) {
$query = "SELECT * FROM cong";
$query .= " WHERE state rLIKE
(SELECT id FROM states WHERE upper(name) rLIKE '{$term}')";
$query .= " OR lga rLIKE
(SELECT id FROM local_govt WHERE upper(name) rLIKE '{$term}')";
$query .= " OR upper(name) rLIKE '{$term}'";
$query .= " OR upper(address) rLIKE '{$term}'";
$query .= " OR upper(sort_code) rLIKE '{$term}'";
$query .= " OR upper(pin) rLIKE '{$term}'";
$query .= " OR upper(zip) rLIKE '{$term}'";
$query .= " OR upper(min) rLIKE '{$term}'";
$query .= " OR upper(sec) rLIKE '{$term}'";
$query .= " OR upper(min_phone) rLIKE '{$term}'";
$query .= " OR upper(sec_phone) rLIKE '{$term}'";
$result = mysql_query($query);
confirm_query($result);
return $result;
}
function confirm_query($query) {
if (!$query) {
die("Database query failed : " . mysql_error());
}
}
?>
The problem now is that, it searches some terms and comes up with accurate results, but for some specific terms like local_govt and state names it pops an error:
(Database query failed : Subquery returns more than 1 row)
Please I need your help as I don't have any idea how to write the code better than that.
Thanks.
You have subequeries in the states and local_govt portions of the WHERE statement. Presumably there are rows for a given value of $term where those queries will return a resultset of more than one row. Because you are using rLIKE, which expects to evaluate against one value (rather than multiple), the overall query will error out.
Instead, refactor as follows:
$query .= " WHERE state IN (SELECT id FROM states WHERE upper(name) rLIKE '{$term}')";
$query .= " OR lga IN (SELECT id FROM local_govt WHERE upper(name) rLIKE '{$term}')";
this will account for that contingency.
Please note that the query as written is unlikely to be very performant. Since you are scanning many different columns, it would be best to try not to use regex here, because the optimizer won't be able to leverage indices. Ideally, reduce it to a constant, so that you can use:
SELECT * FROM cong WHERE $term IN (upper(name), upper(address)...)
but that may not be possible given your requirements. If that's the case, I would probably look at the design of your program and try to split the query into a lookup against one column at most from the application side, e.g.:
SELECT * FROM cong WHERE $term rLIKE upper(NAME)
There error is here:
$query .= " WHERE state rLIKE
(SELECT id FROM states WHERE upper(name) rLIKE '{$term}')";
$query .= " OR lga rLIKE
(SELECT id FROM local_govt WHERE upper(name) rLIKE '{$term}')";
rlike is a regex, but in the end boils down to a straight comparison of values. Your subqueries are returning multiple rows, so in a re-writen fashion, your query would be something more like:
WHERE state = a,b,c,...
OR lga = z,y,x,...
You're trying to do an equality test against multiple values, which is confusing the database. Equality tests are for SINGLE values, e.g. a=b, not a=b,c.

Return row if non existent in other table

I have 2 tables
members
=======
id
f_name // get these values.
l_name
email
friends
=======
to
from
The users's I'd is the value $member_id, if it is present in "to" or "from" I want it to not return the other value of that row, so only non-friends are shown.
Im creating a page to allow members to search through the database to add their friends by email, or name.
I'd like to return all rows from members where there is no record of the userid in either the "to" or "from" columns of the friends table.
I know how to do most basic mysql but joins are an issue for me, i don't really understand how to write them, if anyone can help me out and maybe even explain it a bit that would be great!
My query now:
$query = "SELECT * FROM members WHERE f_name LIKE '%" . $word . "%' OR l_name LIKE '%" . $word . "%' OR mc_name LIKE '%" . $word . "%' OR email LIKE '%" . $word . "%' LIMIT 10";
//Where $word is the search term.
Modified Query
SELECT * FROM members WHERE id NOT IN (SELECT to FROM friends WHERE frm=$member_id) AND $member_id NOT IN (SELECT frm FROM friends WHERE to=$member_id) AND f_name LIKE '%" . $word . "%' OR l_name LIKE '%" . $word . "%' OR mc_name LIKE '%" . $word . "%' OR email LIKE '%" . $word . "%' LIMIT 10
Edited
So, suppose a user with id 3:
SELECT *
FROM members
WHERE id <> 3
AND id NOT IN (SELECT to FROM friends WHERE from = 3)
AND id NOT IN (SELECT from FROM friends WHERE to = 3)
[Other conditions....];
OR
SELECT *
FROM members
LEFT JOIN friends
ON (members.id = friends.to
OR members.id = friends.from)
AND (members.to = 3
OR members.from = 3)
WHERE friends.to IS NULL
AND id <> 3
[Other conditions....];
Maybe consider change the name of your "from" and "to" columns, to avoid confusion, because FROM and TO are reserved words.
Also, you can use the reserved word EXPLAIN , before the query, to see the difference of the number of rows being fetched.
You might be able to do that using an LEFT Join, which will return all rows from members even if there's no corresponding row on the friend's table.
Something like this:
SELECT a.*, f.* FROM members m LEFT JOIN friends f
A great article to understand the many joins there are is this one.

Mysql Query Selecting Results With A Distinct Column Value

I have a table that displays books. However I would like only only 1 book to be shown per unique email address (strContactEmail). I tried the below query, it didn't work. Any help greatly appreciated.`
$sql = "SELECT lngbookid, strTitle, strAuthor, strcoursecode, strISBN, ".
" strcontactname, DISTINCT(strContactEmail) AS strContactEmail, ".
" curPrice, ysnsalerent, dtmpostdate, memcomments, school, ".
" ASIN, BookIMG, ISBN10, ISBN13, Updated, ".
" datetime, user_ip, NoOtherBooks ".
" FROM tblbooks ".
" WHERE strcoursecode LIKE '%$search%' ".
" ORDER BY datetime DESC LIMIT 50";
Try a GROUP BY statement:
http://www.w3schools.com/sql/sql_groupby.asp
The easiest way:
SELECT max(lngbookid) as lngbookid,
max(strtitle) as strtitle,
max(strauthor) as strauthor,
max(strcoursecode) as strcoursecode,
max(strisbn) as strisbn,
max(strcontactname) as strcontactname,
strcontactemail,
max(curprice) as curprice,
max(ysnsalerent) as ysnsalerent,
max(dtmpostdate) as dtmpostdate,
max(memcomments) as memcomments,
max(school) as school,
max(asin) as asin,
max(bookimg) as bookimg,
max(isbn10) as isbn10,
max(isbn13) as isbn13,
max(updated) as updated,
max(datetime) as datetime,
max(user_ip) as user_ip,
max(nootherbooks) as nootherbooks
FROM tblbooks
WHERE strcoursecode LIKE '%$search%'
GROUP BY strcontactemail
ORDER BY datetime DESC
LIMIT 50
EDIT
Well, the above was actually too "dummy". Better way to do this is (providing that column "lngbookid" is a primary key):
SELECT a.lngbookid,
a.strtitle,
a.strauthor,
a.strcoursecode,
a.strisbn,
a.strcontactname,
a.strcontactemail,
a.ontactemail,
a.curprice,
a.ysnsalerent,
a.dtmpostdate,
a.memcomments,
a.school,
a.asin,
a.bookimg,
a.isbn10,
a.isbn13,
a.updated,
a.datetime,
a.user_ip,
a.nootherbooks
FROM tblbooks AS a
JOIN (SELECT strcontactemail,
Max(lngbookid) AS lngbookid
FROM tblbooks
GROUP BY strcontactemail) AS b
ON ( a.strcontactemail = b.strcontactemail
AND a.lngbookid = b.lngbookid )

Categories