mysql to codeigniter active record help - mysql

Active record is a neet concept but sometimes I find it difficult to get more complicated queries to work. I find this is at least one place the CI docs are lacking.
Anyway,
This is the sql I wrote. It returns the expected results of quests not yet completed by the user that are unlocked and within the users level requirements:
SELECT writing_quests . *
FROM `writing_quests`
LEFT OUTER JOIN members_quests_completed ON members_quests_completed.quest_id = writing_quests.id
LEFT OUTER JOIN members ON members.id = $user_id
WHERE writing_quests.unlocked =1
AND writing_quests.level_required <= $userlevel
AND members_quests_completed.user_id IS NULL
This is the codeigniter active record query, it returns all quests that are unlocked and within the users level requirement:
$this->db->select('writing_quests.*');
$this->db->from('writing_quests');
$this->db->join('members_quests_completed', 'members_quests_completed.quest_id = writing_quests.id', 'left outer');
$this->db->join('members', "members.id = $user_id", 'left outer');
$this->db->where('writing_quests.unlock', 1);
$this->db->where('writing_quests.level_required <=', $userlevel);
$this->db->where('members_quests_completed.user_id is null', null, true);
I'm guessing there is something wrong with the way I am asking for Nulls. To be thorough, I figured I'd include everything.

I agree that sometimes CI active record can overcomplicate things. Try this for your IS NULL where clause:
$this->db->where('members_quests_completed.user_id IS ','NULL',false);
Try also to enable the profiler or echo the generated query with:
echo $this->db->last_query();
That might shed some light on what the issue is.

Sometimes CI makes it a bit complicated... you can always use $this->db->query("your very long query") to simplify a bit (if I recall correctly strings are still escaped - not sure). It's a personal opinion.

$this->db->where('column IS NOT NULL')
in your case
$this->db->where('members_quests_completed.user_id is not null');

Related

MySQL syntax checking if parameter is null

I am looking for the way to execute MySQL statement checking if given parameter exists. As I remember I can do the following in Oracle to achieve that:
select s.* from Site s
where s.study = :study
and (:enabled is null or s.enabled = :enabled)
is anything like that possible in MySQL too? The same code executes without error but never return any records.
My goal here is to avoid multiple lfs and elses in my java code. It should work the way that the query looks like that when enabled parameter is null:
select s.* from Site s
where s.study = :study
and like that if the parameter is not null:
select s.* from Site s
where s.study = :study
and s.enabled = :enabled
and I want to do that with a single query
I believe this is what you are asking:
SELECT s.* from Site s
WHERE s.study = "some_study"
AND (s.enabled IS NULL OR s.enabled = '' OR s.enabled = "enabled");
Unfortunately it is highly dependent on database driver. My initial query works when run in database tools but doesn't have to when it comes to run it by JPA. So I'm to close this question as it doesn't require further answers. I'm sorry lads for wasting your time.

2 identical mysql select queries in if statement, first works second does not

I have the following 2 mysql select queries within a PHP if statement:
if ($chooselocn =="") {
$query = "
SELECT $table.*, outcodepostcodes.lat, outcodepostcodes.lng
FROM $table
LEFT JOIN outcodepostcodes
ON UPPER($table.postcode)=outcodepostcodes.outcode
WHERE
$where_no_and
AND
(hide='0' OR hide IS NULL OR hide='')
ORDER BY rent $reihenach LIMIT $offset, $rowsPerPage
";
}
else {
$query = "
SELECT $table.*, outcodepostcodes.lat, outcodepostcodes.lng
FROM $table
LEFT JOIN outcodepostcodes
ON UPPER($table.postcode)=outcodepostcodes.outcode
WHERE
$where_no_and
AND
outcodepostcodes.lat <= $latpoint
AND
(hide='0' OR hide IS NULL OR hide='')
ORDER BY rent $reihenach LIMIT $offset, $rowsPerPage
";
}
The first query works but the second returns this error message:
Query failed: 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 'AND outcodepostcodes.lat <= 51.491239000000000 AND (hide='0' OR hide IS NULL OR' at line 8
Even if I remove the:
AND
outcodepostcodes.lat <= $latpoint
from the 2nd query making the two identical I still get similar error msg in the second.
Any ideas greatly appreciated.
You probably have an empty $where_no_and variable, thus your second query gets to contain ... WHERE AND ... which is not valid SQL
I bet the variable $where_no_and causes the issue.
It seems like it is empty causing the WHERE statement to be followed by the AND.
So this line:
WHERE $where_no_and AND outcodepostcodes.lat <= $latpoint AND (hide='0'...
is like this:
WHERE AND outcodepostcodes.lat <= $latpoint AND (hide='0'...
The WHERE AND is not valid syntax.
Even after you removed the part:
AND outcodepostcodes.lat <= $latpoint
Your line would look like this:
WHERE $where_no_and AND (hide='0'...
Which would result in this:
WHERE AND (hide='0'...
Again you have WHERE AND which is not valid syntax.
Try to make sure that the $where_no_and variable is not null or empty.
If your business logic says that this variable COULD be empty then you have to write a couple of more lines of code to handle this case.
Cheers!
This is not a solution but a way to find the bug.
Just put a var_dump($query) after the if(). It's impossible to tell without knowing what the variables actually contain. The dump will be before the query is executed, so you'll see very well what is going on there.
If the query is still apparently correct post the dump here and we'll see.
Why do if/then if the queries are supposed to be identical regardless of the condition?
That said, your variables that form the query are probably different depending on the value of $chooselocn. I would start by dumping the actual query into debugging log or the browser as well as the variables that compose it and it would shed some light.
I should also add the standard warning about watching out for SQL injections.
Thank you, thank you, thank you and a MASSIVE THANK YOU to every one who replied to my question!!!!
#Gordon Linoff, thank you for being the first to respond in the right direction
#David Lavieri, thank you for the useful suggestion
#Tudor Constantin, thank you for being the first to explain the crux of the problem
#pid, thank you for the useful suggestion
#user2399432, a massive thank you for the very lengthy, detailed and exhaustive explanation of EXACTLY what was going on.
While everyone pointed me in the right direction, and unfortunately I was busy elsewhere to follow up on those early suggestions, I must upvote #user2399432 for going to all that trouble to make absolutely sure that it all sunk in and there were no two ways about it.
Just as background info, this is an extremely old site I have come back to work on, after a three year absence from coding, and I noticed that I had the following lines of code in this particular script:
///THIS IS THE CRUCIAL LINE BELOW:
$where_no_and = rtrim($where, 'AND ');
///End of crucial line and then TEST
//var_dump($where_no_and);//VERY USEFUL DIAGNOSTIC! INDISPENSIBLE! MUST RETAIN! DO NOT DELETE!
//echo "#6 City is:".$lc_city;//USEFUL DIAGNOSTIC
So I must have had the same problem those many years ago and dealt with it accordingly using var_dump($where_no_and);
Be that as it may, I was well and truly stuck this time round and your valuable help has knocked down the barricades, SO THANK YOU TO ALL!

Why isn't my SQL LEFT JOIN query working?

It is returning 0 rows when there should be some results.
This is my first attempt at using JOIN but from what I've read this looks pretty much right, right?
"SELECT pending.paymentid, pending.date, pending.ip, pending.payer, pending.type, pending.amount, pending.method, pending.institution, payment.number, _uploads_log.log_filename
FROM pending
LEFT JOIN _uploads_log
ON pending.paymentid='".$_GET['vnum']."'
AND _uploads_log.linkid = pending.paymentid"
I need to return the specified values from each table where both pending.paymentid and _uploads_log.log_filename are equal to $_GET['vnum]
What is the correct way to do this? Why am I not getting any results?
If someone more experienced than me could point me in the right direction I would be much obliged.
EDIT
For pending the primary key is paymentid, for _uploads_log the primary is a col called log_id and log_filename is listed as index.
Try this
SELECT pending.paymentid,
pending.date,
pending.ip,
pending.payer,
pending.type,
pending.amount,
pending.method,
pending.institution,
payment.number,
_uploads_log.log_filename
FROM pending
LEFT JOIN _uploads_log
ON _uploads_log.linkid = pending.paymentid
WHERE _uploads_log.log_filename = '" . $_GET['vnum'] . "'
Your current query is vulnerable with SQL Injection. Please take time to read the article below.
Best way to prevent SQL injection in PHP?
The ON clause only should have the condition to link the two tables especially if it is LEFT JOIN. The WHERE clause then has the actual condition. Otherwise you will get nothing if there is no corresponding entry in _uploads_log. It also is more easy to read in my opinion.
As another remark. It is always better to work with bind parameters to avoid SQL injection.

MySQL order by problems

I have the following codes..
echo "<form><center><input type=submit name=subs value='Submit'></center></form>";
$val=$_POST['resulta']; //this is from a textarea name='resulta'
if (isset($_POST['subs'])) //from submit name='subs'
{
$aa=mysql_query("select max(reservno) as 'maxr' from reservation") or die(mysql_error()); //select maximum reservno
$bb=mysql_fetch_array($aa);
$cc=$bb['maxr'];
$lines = explode("\n", $val);
foreach ($lines as $line) {
mysql_query("insert into location_list (reservno, location) values ('$cc', '$line')")
or die(mysql_error()); //insert value of textarea then save it separately in location_list if \n is found
}
If I input the following data on the textarea (assume that I have maximum reservno '00014' from reservation table),
Davao - Cebu
Cebu - Davao
then submit it, I'll have these data in my location_list table:
loc_id || reservno || location
00001 || 00014 || Davao - Cebu
00002 || 00014 || Cebu - Davao
Then this code:
$gg=mysql_query("SELECT GROUP_CONCAT(IF((#var_ctr := #var_ctr + 1) = #cnt,
location,
SUBSTRING_INDEX(location,' - ', 1)
)
ORDER BY loc_id ASC
SEPARATOR ' - ') AS locations
FROM location_list,
(SELECT #cnt := COUNT(1), #var_ctr := 0
FROM location_list
WHERE reservno='$cc'
) dummy
WHERE reservno='$cc'") or die(mysql_error()); //QUERY IN QUESTION
$hh=mysql_fetch_array($gg);
$ii=$hh['locations'];
mysql_query("update reservation set itinerary = '$ii' where reservno = '$cc'")
or die(mysql_error());
is supposed to update reservation table with 'Davao - Cebu - Davao' but it's returning this instead, 'Davao - Cebu - Cebu'. I was previously helped by this forum to have this code working but now I'm facing another difficulty. Just can't get it to work. Please help me. Thanks in advance!
I got it working (without ORDER BY loc_id ASC) as long as I set phpMyAdmin operations loc_id ascending. But whenever I delete all data, it goes back as loc_id descending so I have to reset it. It doesn't entirely solve the problem but I guess this is as far as I can go. :)) I just have to make sure that the table column loc_id is always in ascending order. Thank you everyone for your help! I really appreciate it! But if you have any better answer, like how to set the table column always in ascending order or better query, etc, feel free to post it here. May God bless you all!
The database server is allowed to rewrite your query to optimize its execution. This might affect the order of the individual parts, in particular the order in which the various assignments are executed. I assume that some such reodering causes the result of the query to become undefined, in such a way that it works on sqlfiddle but not on your actual production system.
I can't put my finger on the exact location where things go wrong, but I believe that the core of the problem is the fact that SQL is intended to work on relations, but you try to abuse it for sequential programming. I suggest you retrieve the data from the database using portable SQL without any variable hackery, and then use PHP to perform any post-processing you might need. PHP is much better suited to express the ideas you're formulating, and no optimization or reordering of statements will get in your way there. And as your query currently only results in a single value, fetching multiple rows and combining them into a single value in the PHP code shouldn't increase complexety too much.
Edit:
While discussing another answer using a similar technique (by Omesh as well, just as the answer your code is based upon), I found this in the MySQL manual:
As a general rule, you should never assign a value to a user variable
and read the value within the same statement. You might get the
results you expect, but this is not guaranteed. The order of
evaluation for expressions involving user variables is undefined and
may change based on the elements contained within a given statement;
in addition, this order is not guaranteed to be the same between
releases of the MySQL Server.
So there are no guarantees about the order these variable assignments are evaluated, therefore no guarantees that the query does what you expect. It might work, but it might fail suddenly and unexpectedly. Therefore I strongly suggest you avoid this approach unless you have some relaibale mechanism to check the validity of the results, or really don't care about whether they are valid.

LEFT JOIN breaks WHERE Clause

I've recently been required to input more information from my database and I've just LEFT JOIN to help me, it works almost perfectly(it does actually get the right field from the other table) but my WHERE clause is nullified giving the user access to both tables without the restriction of my where clause.
MySQL doesn't crap out any errors, so I'm assuming it's something to do with my where clause or something happened in the join.
SELECT * FROM students
LEFT JOIN courses ON students.appliedforCourse = courses.idNumber
WHERE
students.telephone LIKE '%$var'
OR students.email LIKE '%$var'
OR students.address like'%$var%'
OR (CONCAT(students.firstName,' ',students.lastName) LIKE '%$var%')
AND addedBy ='$userid'
LIMIT $s,limit
The query itself is correct (although really inefficient due to ORs and % % [ indexes will not be used] ).
I would suggest to echo the query, are you sure that $var is evaluated correctly ? Try to run the query directly in mysql (via phpmyadmin for example or using console).
I suspect that simply you did not set $var value. Then condition e.g. students.telephone LIKE '%$var' will become students.telephone LIKE '%' (always true for not null address), which will match every record of the join , exactly what you are getting.