Mysql - Simple select query returns duplicate values? - php

This is how the query looks like: (simplified)
SELECT * FROM posts WHERE user='37' ORDER BY date DESC
I currently only ave one row in that table, but still for some reason it returns two rows that are exactly the same. At first i thought i messed up with the loop, but i tried printing the returned array out with print_r() and it actually returns two rows.
I tried searching, but i didn't find any similar issues. I do however remember that a friend of mine had the same issue at school, so i'm sure we aint the only ones. I probably just didn't use the right search terms, heh.

If you have only one record (verify this), it has to be application logic that is duplicating the returned values.

Are you sure you only have one row in the table? If so, it seems like the problem must be happening outside of SQL.
What are you doing outside of this query? That seems like the likely source of the issue. You mention a loop: could you be adding the query result to your array twice? Or is the array persisting between calls without being reinitialized (in other words, the result of a previous query is remaining in the array when you don't expect it to)?

limit 1 is your friend :)
Try adding it to the end of your query.

Related

PHP / MySQL Run Function From Multiple Results In Array

I'm not sure that I have the terminology correct but basically I have a website where members can subscribe to topics that they like and their details go into a 'favorites' table. Then when there is an update made to that topic I want each member to be sent a notification.
What I have so far is:
$update_topic_sql = "UPDATE topics SET ...My Code Here...";
$update_topic_res = mysqli_query($con, $update_topic_sql)or die(mysqli_error());
$get_favs_sql = "SELECT member FROM favourites WHERE topic = '$topic'";
$get_favs_res = mysqli_query($con, $get_favs_sql);
//Here I Need to update the Members selected above and enter them into a notes table
$insert_note_sql = "INSERT INTO notes ....";
Does anyone know how this can be achieved?
Ok, so we've got our result set of users. Now, I'm going to assume from the nature of the question that you may be a bit of a newcomer to either PHP, SQL(MySQL in this case), web development, or all of the above.
Your question part 1:
I have no idea how to create an array
This is easier than what you may think, and if you've already tried this then I apologize, I don't want to insult your intelligence. :)
Getting an array from a mysqli query is just a matter of a function call and a loop. When you ran your select query and saved the return value to a variable, you stored a mysqli result set. The mysqli library supports both procedural and object oriented styles, so since you're using the procedural method, so will I.
You've got your result set
$get_favs_res = mysqli_query($con, $get_favs_sql);
Now we need an array! At this point we need to think about exactly what our array should be of, and what we need to do with the contents of the request. You've stated that you want to make an array out of the results of the SELECT query
For the purposes of example, I'm going to assume that the "member" field you've returned is an ID of some sort, and therefore a numeric type, probably of type integer. I also don't know what your tables look like, so I'll be making some assumptions there too.
Method 1
//perform the operations needed on a per row basis
while($row = mysqli_fetch_assoc($get_favs_res)){
echo $row['member'];
}
Method 2
//instead of having to do all operations inside the loop, just make one big array out of the result set
$memberArr = array();
while($row = mysqli_fetch_assoc($get_favs_res)){
$memberArr[] = $row;
}
So what did we do there? Let's start from the beginning to give you an idea of how the array is actually being generated. First, the conditional in the while loop. We're setting a variable as the loop condition? Yup! And why is that? Because when PHP (and a lot of other languages) sets that variable, the conditional will check against the value of the variable for true or false.
Ok, so how does it get set to false? Remember, any non boolean false, non null, non 0 (assuming no type checking) resolves to true when it's assigned to something (again, no type checking).
The function returns one row at a time in the format of an associative array (hence the _assoc suffix). The keys to that associative array are simply the names of the columns from the row. So, in your case, there will be one value in the row array with the name "member". Each time mysqli_fetch_assoc() is called with your result set, a pointer is pointed to the next result in the set (it's an ordered set) and the process repeats itself. You essentially get a new array each time the loop iterates, and the pointer goes to the next result too. Eventually, the pointer will hit the end of the result set, in which case the function will return a NULL. Once the conditional picks up on that NULL, it'll exit.
In the second example, we're doing the exact same thing as the first. Grabbing an associative array for each row, but we're doing something a little differently. We're constructing a two dimensional array, or nested array, of rows. In this way, we can create a numerically indexed array of associative arrays. What have we done? Stored all the rows in one big array! So doing things like
$memberArr[0]['member'];//will return the id of the first member returned
$memberArr[1]['member'];//will return the id of the second member returned
$lastIndex = sizeof($memberArr-1);
$memberArr[$lastIndex]['member'];//will return the id of the last member returned.
Nifty!!!
That's all it takes to make your array. If you choose either method and do a print_r($row) (method 1) or print_r($memberArr) (method 2) you'll see what I'm talking about.
You question part 2:
Here I Need to update the Members selected above and enter them into a notes table
This is where things can get kind of murky and crazy. If you followed method 1 above, you'd pretty much have to call
mysqli_query("INSERT INTO notes VALUES($row['member']);
for each iteration of the loop. That'll work, but if you've got 10000 members, that's 10000 inserts into your table, kinda crap if you ask me!
If you follow method two above, you have an advantage. You have a useful data structure (that two dim array) that you can then further process to get better performance and make fewer queries. However, even from that point you've got some challenges, even with our new processing friendly array.
The first thing you can do, and this is fine for a small set of users, is use a multi-insert. This just involves some simple string building (which in and of itself can pose some issues, so don't rely on this all the time) We're gonna build a SQL query string to insert everything using our results. A multi insert query in MySQL is just like a normal INSERT except for one different: INSERT INTO notes VALUES (1),(2),(x)
Basically, for each row you are inserted, you separate the value set, that set delineated by (), with a comma.
$query = 'INSERT INTO notes VALUES ';
//now we need to iterate over our array. You have your choice of loops, but since it's numerically indexed, just go with a simple for loop
$numMembers = sizeof($memberArr);
for($i = 0; $i < $numMembers; $i++){
if($i > 0){
$query .= ",({$membersArr[$i]['member']})";//all but the first row need a comma
}
else {
$query .= "({$membersArr[$i]['member']})";//first row does not need a comma
}
}
mysqli_query($query);//add all the rows.
Doesn't matter how many rows you need to add, this will add them. However, this is still going to be a costly way to do things, and if you think your sets are going to be huge, don't use it. You're going to end up with a huge string, TONS of string concats, and an enormous query to run.
However, given the above, you can do what you're trying to do.
NOTE: These are grossly simplified ways of doing things, but I do it this way because I want you to get the fundamentals down before trying something that's going to be way more advanced. Someone is probably going to comment on this answer without reading this note telling me I don't know what I'm doing for going about this so dumbly. Remember, these are just the basics, and in no way reflect industrial strength techniques.
If you're curious about other ways of generating arrays from a mysqli result set:
The one I used above
An even easier way to make your big array but I wanted to show you the basic way of doing things before giving you the shortcuts. This is also one of those functions you shouldn't use much anyway.
Single row as associative(as bove), numeric, or both.
Some folks recommend using loadfiles for SQL as they are faster than inserts (meaning you would dump out your data to a file, and use a load query instead of running inserts)
Another method you can use with MySQL is as mentioned above by using INSERT ... SELECT
But that's a bit more of an advanced topic, since it's not the kind of query you'd see someone making a lot. Feel free to read the docs and give it a try!
I hope this at least begins to solve your problem. If I didn't cover something, something didn't make sense, or I didn't your question fully, please drop me a line and I'll do whatever I can to fix it for you.

Iterating through a sub-section of result resource in PHP-MySQL

In my program I launch an SQL query and get back a result resource. I then iterate through the rows of this result resource using the mysql_fetch_array() function and use the contents of the fields of each row to construct a further SQL query.
The result of launching this second query is the first set of results that I want. However, because the number of results produced by doing this is not many I want to make the search less specific by dropping the last record used to make the query.
e.g. the query which produces the first set of results I want could be:
SELECT uid FROM users WHERE (gender=male AND relationship_status=single
AND shoe_size=10)
I would then want to drop the last record so that my query became:
SELECT uid FROM users WHERE (gender=male AND relationship_status=single)
I have already written code to produce the first query but as I mentioned above I use the mysql_fetch_array function to iterate through ALL of the records. In subsequent "rounds" I only want to iterate through successively less records so that my query is less specific. How can I do this?
This seems like an very inefficient method too - so I'm welcome to any simple ideas which might make it more efficient.
EDIT: Thanks for the reply - Yeah I am actually doing this in my program. I am basically trying to implement a basic search algorithm by taking all the preferences a user has specified in the DB and using it to form a query to look for people with those preferences. So the first time search using all the criteria, then on successive attempts search using one less criteria and negate the user ids which were previously returned. At the moment I am constructing the query from scratch for each "round", but I want to find a way I can do this using the last query
Using the queries above, you could do:
SELECT uid
FROM users
WHERE uid NOT IN (
SELECT uid
FROM users
WHERE
(gender=male
AND relationship_status=single
AND shoe_size=10)
)
This will essentially turn your first query into a sub-query, and use that to negate the results returned. Ie, it will return all the rows, NOT IN the first query.

deleting only one row when there are duplicates

Regarding this line
database::query("DELETE FROM bo WHERE name='{$this->_protected_arr[a1]}' AND email='$_SESSION[email]'");
How can I update it so that it only deletes one row instead of all of them.
Also, the syntax looks off, is there any way not to use the {}.
Also, normally PHP calls warnings if ther are no apostrohpes in associative array but it does not pick it up in this case. So this seems wrong as well.
So this is actually 3 questions.
How can I update it so that it only deletes one row instead of all of them.
Use a LIMIT 1 clause at the end of the query.
the syntax looks off
It's correct. It's what PHP calls the "complex (curly) syntax" for variable parsing in strings:
http://php.net/manual/en/language.types.string.php#language.types.string.parsing.complex
You can of course use string concatenation there if you prefer.
You should use LIMIT 1. The question is which row will be caught in LIMIT 1 selection :)
The best case scenario is that your database::query supports placeholders.
Concerning curly brakets, just add apostrophes to the array key
database::query("DELETE FROM bo WHERE name='{$this->_protected_arr['a1']}' AND email='{$_SESSION['email']}'");
If you don't wanna use curly brakets, then just concatenate strings like "DELETE ...".$this->property." AND ...".
database::query("DELETE FROM bo WHERE name='{$this->_protected_arr[a1]}' AND email='$_SESSION[email]' LIMIT 1");
As everyone else suggested, adding a LIMIT 1 to the end of your query will make it so that only one row is deleted. However, given you're using static classes here, I'm guessing you know enough to understand why everything in a database should have its own unique identifier and I'm curious why you aren't using that as part of your criteria for determining what you delete? Or there should at least be some other comparison you can do, like running a select statement that pulls all the rows and then loops over the results, setting them to a multidimensional array where you can then compare the column values for the multiple rows and use that comparison to decide which row to keep. If you're getting multiple entries maybe look into preventing users from double-clicking by using jquery to hide the button or gray it out as soon as it's been clicked. Just some thoughts...

How do I do a boolean OR in solr?

I am completely new at this...
I am using durpal with the apachesolr module.
I have the following filter:
( tid:19895 OR tid:19937 ) AND type:poster"
This does not return any results, but if I have the following, it returns the results as expected
( tid:19937 ) AND type:poster"
EDIT: This is all in a filter. I need to have a list of tids that it could be along with having type be poster. So it has to be type:poster AND one of the following tids: 19895, 19937
although:
((tid:(19895 OR 19937)) AND type:poster) should work irrespective of defaultOperator being OR/AND
adding the clauses as separate filter is better, so add two filters as
tid:(19895 OR 19937)
type:poster
should filter out the results working as AND and also cache the results for this filter separately for improved query performance for new queries using this filter.
I think its important to note that SOLR isn't a database. Its a search engine and therefore isn't going to work like a database query you may be used to.
adding a + means the field is required
+tid:19895 would mean that TID field is required to equal exactly 19895
The * is the wildcard so adding it means your field would contain the value but not necessarily equal the value.
tid:19895* would mean TID field contains 19895
It looks like the issue is defining the field twice in the same filter???
So try removing the second "tid:"
In my testing I did this:
My SOLR query is:
+type:poster
And the Filter is:
tid:19895 OR 19937
Here is another stackoverflow question similar to yours since you obviously have a different syntax than I use.
using OR and NOT in solr query
As FYI if defalut boolen clause is "OR" then give the query like this
((tid:(19895 19937)) AND type:poster) This working fine i tested

PHP MySQL while loop not returning anything

I'm quite new to php and mysql so hopefully someone with more experience will be able to give me some guidance here.
I have the following code:
<?php
$npcname = $_GET['npcname'];
$npcinfo="SELECT * from npcs where name='$npcname'";
$npcinfo2=mysql_query($npcinfo) or die("could not get npc!");
$npcinfo3=mysql_fetch_array($npcinfo2);
$listquests = "SELECT * from quests where npcid = '$npcinfo3[npcid]'";
$listquests2 = mysql_query($listquests) or die("No Quests to list");
$listquests3=mysql_fetch_array($listquests2);
echo "<b>Quests Available for ".$npcname."</b><br>";
while($row=mysql_fetch_array($listquests2)) {
echo $row['name'];
}
?>
To go with this I have some tables whcih look like this:
npcs
name|location|npcid
quests
name|qid|npcid
So a quest is associated to a NPC via the npcid field.
I have one entry in each table.
Bob|Scrapyard|1
AND
Sort Scrap Metal|1|1
As you can see the quest and Bob both share the npcid of 1.
In my loop I am trying to list all of the quests for Bob. However on running the code I do not get any quests listed.
If I put the code:
$listquests3['name'];
Outside of my loop it successfully displays "Sort Scrap Metal" as expected. The reason I have used the loop is to display multiple quests when I add them.
If somebody could be kind enough to take a look at the code and tell me what I have done wrong I would be grateful.
Thank You.
It may be a good idea to print out the SQL and run this against your database to see what results you get.
Looking at this it looks like there may only be one result which is fetched in the
$listquests3=mysql_fetch_array($listquests2);
line. Since there are no more results there is nothing to loop over.
The results are already fetched, since there is only one rule and you fetched it in $listquests3 :). It will work if you remove that line I think.
You need to do a INNER JOIN or a LEFT JOIN. Yes after carefully seeing the question again, I found that when doing the "mysql_fetch_array()" code for the first time (just before the "while" loop), the value of the variable "$listquests2" gets lost. So the "while" loop does nothing fruitful.
You must remove this single line for variable "$listquests3".
You only have one row, and you fetched that row when you called mysql_fetch_array the first time. When you call it the second time, there are no more rows to fetch in the result set, the function returns false and your loop exits.
This statement: "$listquests3=mysql_fetch_array($listquests2);" already fetches the first. Sicne you have only one, there's nothing more to fetch, so the next call to mysql_fetch_array will return nothing.
That should fix it, but for your own 'experience', this might be a good moment to start learning about MySQL joins (LEFT JOIN in particular). You can easily find a lot about it on the internet!

Categories