Using WHERE, AND, & OR in same MySQL query help - php

Will this query work? Is it most efficient?
SELECT * FROM `posts`
WHERE MATCH (`title`, `body`)
AGAINST ('search terms' IN BOOLEAN MODE)
AND `price` BETWEEN '100' AND '1000'
AND (`postinto` = 'cat1' OR `postinto` = 'cat2')
AND (`location` = 'loc1' OR `location` = 'loc2')
ORDER BY `id` DESC
LIMIT 0, 100;
Note: values for postinto and location will be contained in a PHP arrays, so if this will work I plan on looping their the arrays to generate the query terms. Is there a way to pass the entire array to MySQL? Also, these two conditions have a possibility of being quite long (a dozen values). Is there a better way?
specifically my question is about this:
AND (`postinto` = 'cat1' OR `postinto` = 'cat2')
AND (`location` = 'loc1' OR `location` = 'loc2')
an example of possible values would be "community|groups", "buy-sell-trade|electronics" where before the | is a category and after | is a sub category. If I am searching an entire category I would want to change that part of the query to:
AND (`postinto` LIKE 'category|%' OR `postinto` = 'this'
I have the proper indexes for the fulltext search, my question is about the OR clause. Is there a maximum number of times you can use OR in one query? Is this syntax even correct?
Thanks

Actually there is. The IN statement can help you here.
Your query would then become like this:
SELECT * FROM `posts`
WHERE MATCH (`title`, `body`)
AGAINST ('search terms' IN BOOLEAN MODE)
AND `price` BETWEEN '100' AND '1000'
AND `postinto` IN ('cat1', 'cat2')
AND `location` IN ('loc1', 'loc2')
ORDER BY `id` DESC
LIMIT 0, 100;

You can use AND and OR in a WHERE as often as you want, but to keep your sanity you need to obey a few simple rules.
1) Do not mix them, constructing a query with WHERE AND OR AND OR will probably work but the results will be unpredictable and the result set may contain multiple instances of the same record! (You are effectively turning the OR construct into an EITHER!
2) I would recommend always putting all of the OR statements before any AND statements. It will be easier for you to understand and for anybody else to maintain.
3) It is probably not a necessity but I always if possible enclose the OR statements in brackets.
I Maintain a Sports database and run some queries that can have multiple options in the select.
Hopefully the following examples will simplify the explanation.
// Make the query to retrieve the set of Singles Games.
$query = "SELECT * from Games WHERE Season = '$season'
AND GameNo < 8
OR Player1 = '$playerName' ORDER by MatchNo ASC";
$result = #mysql_query($query); // Run the query.
$num = mysql_num_rows($result);
This will run, it is syntactically correct but the results will be unpredictable!
If in these circumstances you wish to put the OR after the AND then you need to 'AND' the OR.
// Make the query to retrieve the set of Singles Games.(This is correct)
$query = "SELECT * from Games WHERE (Player1 = '$playerName'
OR Player3 = '$playerName') AND Season = '$season'
AND GameNo < 8 ORDER by MatchNo ASC";
$result = #mysql_query($query); // Run the query.
$num = mysql_num_rows($result);
This will run and produce a list of Singles games in which a player has participated.
The following is the same statement with the OR after the AND. Notice you need to AND
the OR to the end of the statement.
// Make the query to retrieve the set of Singles Games.(This is also correct)
$query = "SELECT * from Games WHERE Season = '$season'
AND GameNo < 8 AND (Player1 = '$playerName'
OR Player3 = '$playerName') ORDER by MatchNo ASC";
$result = #mysql_query($query); // Run the query.
$num = mysql_num_rows($result);
This will run and produce a list of Singles games in which a player has participated.
Finally a couple of examples, why I prefer the OR first
$query = "SELECT * from Games WHERE (Player1 = '$playerName'
OR Player2 = '$playerName' OR Player3 = '$playerName'
OR Player4 = '$playerName') AND Season = '$season'
AND GameNo > 7 ORDER by MatchNo ASC";
$result = #mysql_query($query); // Run the query.
$num = mysql_num_rows($result);
This will produce a result set containing all the League doubles that a player has participated in. Either as home player1 or 2, or away player 3 or 4.
To produce a query with multiple separate OR statements. Then place an AND between the OR
statements.
$query = "SELECT * from CupGames WHERE (Player1 = '$playerName'
OR Player2 = '$playerName' OR Player3 = '$playerName'
OR Player4 = '$playerName' OR Player5 = '$playerName'
OR Player6 = '$playerName') AND (CupID = 'CS' OR CupID = 'ND')
AND Season = '$season' AND GameNo < 4 ORDER by CupRound ASC";
$result04 = #mysql_query($query); // Run the query.
$num = mysql_num_rows($result04);
This will produce a result set containing all the Cup Triples that a player has participated in. Either as home player1,2 or 3, or away player 4, 5 or 6. In either of
the two selected cups. CS(Coronation Shield) or ND(Norman Day Cup) for the selected season.
If you wish to keep your hair I suggest following these rules.
(I have retired from IT and have a full head of hair!)
Best of Luck!

Related

PHP Calculate rank from database

I got a little problem, I've got a database, in that database are different names, id, and coins. I want to show people their rank, so your rank has to be 1 if you have the most coins, and 78172 as example when your number 78172 with coins.
I know I can do something like this:
SELECT `naam` , `coins`
FROM `gebruikers`
ORDER BY `coins` DESC
But how can I get the rank you are, in PHP :S ?
You can use a loop and a counter. The first row from MySql is going the first rank,I.e first in the list.
I presume you want something like:
1st - John Doe
2nd - Jane Doe
..
..
right?
See: http://www.if-not-true-then-false.com/2010/php-1st-2nd-3rd-4th-5th-6th-php-add-ordinal-number-suffix
Helped me a while ago.
You could use a new varariable
$i = "1";
pe care o poti folosi in structura ta foreach,while,for,repeat si o incrementezi mereu.
and you use it in structures like foreach,while,for,repeat and increment it
$i++;
this is the simplest way
No code samples above... so here it is in PHP
// Your SQL query above, with limits, in this case it starts from the 11th ranking (0 is the starting index) up to the 20th
$start = 10; // 0-based index
$page_size = 10;
$stmt = $pdo->query("SELECT `naam` , `coins` FROM `gebruikers` ORDER BY `coins` DESC LIMIT {$start}, {$page_size}");
$data = $stmt->fetchAll();
// In your template or whatever you use to output
foreach ($data as $rank => $row) {
// array index is 0-based, so add 1 and where you wanted to started to get rank
echo ($rank + 1 + $start) . ": {$row['naam']}<br />";
}
Note: I'm too lazy to put in a prepared statement, but please look it up and use prepared statements.
If you have a session table, you would pull the records from that, then use those values to get the coin values, and sort descending.
If we assume your Session table is sessions(session_id int not null auto_increment, user_id int not null, session_time,...) and we assume that only users who are logged in would have a session value, then your SQL would look something like this: (Note:I am assuming that you also have a user_id column on your gebruikers table)
SELECT g.*
FROM gebruikers as g, sessions as s WHERE s.user_id = g.user_id
ORDER BY g.coins DESC
You would then use a row iterator to loop through the results and display "1", "2", "3", etc. The short version of which would look like
//Connect to database using whatever method you like, I will assume mysql_connect()
$sql = "SELECT g.* FROM gebruikers as g, sessions as s WHERE s.user_id = g.user_id ORDER BY g.coins DESC";
$result = mysql_query($sql,$con); //Where $con is your mysql_connect() variable;
$i = 0;
while($row = mysql_fetch_assoc($result,$con)){
$row['rank'] = $i;
$i++;
//Whatever else you need to do;
}
EDIT
In messing around with a SQLFiddle found at http://sqlfiddle.com/#!2/8faa9/6
I came accross something that works there; I don't know if it will work when given in php, but I figured I would show it to you either way
SET #rank = 0; SELECT *,(#rank := #rank+1) as rank FROM something order by coins DESC
EDIT 2
This works in a php query from a file.
SELECT #rank:=#rank as rank,
g.*
FROM
(SELECT #rank:=0) as z,
gebruikers as g
ORDER BY coins DESC
If you want to get the rank of one specific user, you can do that in mysql directly by counting the number of users that have more coins that the user you want to rank:
SELECT COUNT(*)
FROM `gebruikers`
WHERE `coins` > (SELECT `coins` FROM `gebruikers` WHERE `naam` = :some_name)
(assuming a search by name)
Now the rank will be the count returned + 1.
Or you do SELECT COUNT(*) + 1 in mysql...

Combining queries for speed

I started using only 1 query but then I wanted some to show up more than others so I ended up doing more queries but it, really slows down the load time, is there a way I can do them all in a single query but have them all with their separate variable name?
$sql23 = "SELECT * FROM monsters WHERE rare='0' AND level<='".$row['level']."' ORDER BY RAND() LIMIT 1;";
$result23 = mysqli_query($link,$sql23) or die(mysqli_error());
$battle_get23 = mysqli_fetch_array($result23);
$boss = "SELECT * FROM monsters WHERE rare='1' AND level<='".$row['level']."' ORDER BY RAND() LIMIT 1;";
$boss = mysqli_query($link,$boss) or die(mysqli_error());
$boss = mysqli_fetch_array($boss);
$rare1 = "SELECT * FROM monsters WHERE rare='2' AND level<='".$row['level']."' ORDER BY RAND() LIMIT 1;";
$rare1 = mysqli_query($link,$rare1) or die(mysqli_error());
$rare1 = mysqli_fetch_array($rare1);
$rare2 = "SELECT * FROM monsters WHERE rare='3' AND level<='".$row['level']."' ORDER BY RAND() LIMIT 1;";
$rare2 = mysqli_query($link,$rare2) or die(mysqli_error());
$rare2 = mysqli_fetch_array($rare2);
$rare3 = "SELECT * FROM monsters WHERE rare='4' AND level<='".$row['level']."' ORDER BY RAND() LIMIT 1;";
$rare3 = mysqli_query($link,$rare3) or die(mysqli_error());
$rare3 = mysqli_fetch_array($rare3);
$rare4 = "SELECT * FROM monsters WHERE rare='5' AND level<='".$row['level']."' ORDER BY RAND() LIMIT 1;";
$rare4 = mysqli_query($link,$rare4) or die(mysqli_error());
$rare4 = mysqli_fetch_array($rare4);
$rare5 = "SELECT * FROM monsters WHERE rare='6' AND level<='".$row['level']."' ORDER BY RAND() LIMIT 1;";
$rare5 = mysqli_query($link,$rare5) or die(mysqli_error());
$rare5 = mysqli_fetch_array($rare5);
$rare6 = "SELECT * FROM monsters WHERE rare='7' AND level<='".$row['level']."' ORDER BY RAND() LIMIT 1;";
$rare6 = mysqli_query($link,$rare6) or die(mysqli_error());
$rare6 = mysqli_fetch_array($rare6);
$rare7 = "SELECT * FROM monsters WHERE rare='8' AND level<='".$row['level']."' ORDER BY RAND() LIMIT 1;";
$rare7 = mysqli_query($link,$rare7) or die(mysqli_error());
$rare7 = mysqli_fetch_array($rare7);
$rare8 = "SELECT * FROM monsters WHERE rare='9' AND level<='".$row['level']."' ORDER BY RAND() LIMIT 1;";
$rare8 = mysqli_query($link,$rare8) or die(mysqli_error());
$rare8 = mysqli_fetch_array($rare8);
$rare9 = "SELECT * FROM monsters WHERE rare='10' AND level<='".$row['level']."' ORDER BY RAND() LIMIT 1;";
$rare9 = mysqli_query($link,$rare9) or die(mysqli_error());
$rare9 = mysqli_fetch_array($rare9);
First. You are barking the wrong tree.
Combining a number of slow queries in one won't make them fast. Overhead for running a query is NOT that important as most php users think. If query itself is fast, no matter how many times it is called (within sane numbers of course). If query is slow, no combining would help.
You have to take care for the queries themselves instead of trying to combine them. Thus, to solve your problem you have to provide a lot more info:
Define "really slows" in certain numbers. For all queries in one and for the every single query.
Provide database schema and data amounts.
Provide result of one of the queries run perpended with EXPLAIN keyword
You could give your monsters a random number once a day or once an hour, so
update monsters set sortorder=rand();
It might help to have an index on that key sortorder.
Then you could calculate for every read a random number in php:
$rand = mt_rand(0,1); // or however mt_rand() is called
Then you could select:
$sql23 = "SELECT * FROM monsters WHERE rare='0' AND level<='".intval($row['level'])."' ORDER BY (sortorder-$rand) DESC LIMIT 1;";
$result23 = mysqli_query($link,$sql23) or die(mysqli_error());
$battle_get23 = mysqli_fetch_array($result23);
From SQL it is easy to create a solution with only one select, but that needs a temporary table to support undisturbed random.
Use a category-table
CREATE TABLE raretype (id int(11), name varchar(255), sortkey float default 0, primary key(id));
Put your rares in there:
INSERT INTO raretype (id, name) values (0, 'battle_get23'),(1,'boss'),(2,'rare1'),...
Create a random sortorder for your read:
DROP TEMPORARY TABLE IF EXISTS tmp_rare_sort;
CREATE TEMPORARY TABLE tmp_rare_sort (raretype int(11) not null primary key, sortorder float);
INSERT INTO tmp_rare_sort SELECT id, rand() from raretype;
Read them out:
SELECT rt.name as type, m.*
FROM raretype rt
INNER JOIN tmp_rare_sort rts on rts.raretype = rt.id
LEFT JOIN monsters m on m.id =
( select id from monsters imo where imo.rare = rt.id
and imo.level <= {intval($level)}
order by abs(sortorder - rts.sortorder) desc
limit 1
)
You should read out an array as a result:
$allMonsters = array();
while($line = $rs->next()){
$allMonsters[$line['type']] = $line;
}
So that $allMonsters['boss'] gives the boss (with an additional field 'type', that should not hurt).
If you really want it as single variables, you could extract() this array.
Hope that helps!
If some syntax errors, please create a SQL-fiddle and I'll check. Just no time to create the tables myself. ;-)
1) take out all of your order by RAND() and limit 1
2) do randomization after query
$bossSql = "SELECT * FROM monsters WHERE rare='1' AND level<='".$row['level']."' ";
$bossRs = mysqli_query($link,$bossSql) or die(mysqli_error());
$bossArray = mysqli_fetch_array($bossRs);
//just saw you had limit in your sql, edited
//shuffle($bossArray);
$randomBosskey = array_rand($bossArray);
$randomBoss = $bossArray[$randomBossKey];
edit: thank you #flaschenpost for pointing out the potential problem, which inspired me to come up with a possibly quicker solution (under certain assumption which might not be true)
Solution 2) cache the table
From the looks of the code I think there is a high chance that you would need to do the query for monster action many times throughout the whole program lifetime.
Instead of getting a new random monster from DB everytime, will it be better to do full query:
SELECT * FROM monsters
save it in a variable and then pick the a random monster according to rarity and level from the same variable everytime you need a monster? But this method takes up unknown amount of memory depends on your table size and might be faster/slower than your original depending on how many "random monster" query you are actually using in your program. It might also depends on the "power" of your machine, assuming your DB and server are not in the same machine and the 2 machine have significant difference in processing power.

mysql + php: Selecting multiple random results

I've been looking for this for a while but with no success.
I am trying to implement a recomendation bar, for example like in youtube, when you are seeing a video it shows the list or recommended videos on the right.
At this moment I am using this method:
$offset_result = mysql_query( " SELECT FLOOR(RAND() * COUNT(*)) AS `offset` FROM `$tablename` ");
$offset_row = mysql_fetch_object($offset_result );
$offset = $offset_row->offset;
$result_rand = mysql_query( " SELECT * FROM `$tablename` LIMIT $offset, 9 " );
This works fine, but sometimes doesn't show any result, and the problem is also that its not completely random, because it shows for example the first ID as 200, so the next result will be id 201 and then 202 and so.
I would like to know if there is a way to show this 9 randon results, for example 1º result id 500, 2º result id 10, 3º result id 788, etc etc?
Thank you
Not entirely sure this answers what you are looking for, but try:
$result_rand = mysql_query("SELECT * FROM " . $tablename . " ORDER BY RAND() LIMIT 9");
You can use php rand() function to create 5 numbers and save them in an array:
http://php.net/manual/en/function.rand.php
<?php
$rand_array = array();
for($i=1;$i<5;$i++) {
$rand_array[$i] = rand(0,500);
}
?>
and after that create a query with every int with a foreach loop and work with your data.
<?php
foreach ($rand_array as $integer) {
$q = "SELECT * from $tablename WHERE id='$integer';";
}
?>
Does this helps?
First you should use mysqli_ functions instead of mysql_ because the latter is deprecated. Second use order by rand() to get random rows:
$rand_result = mysqli_query( "SELECT * FROM $tablename ORDER BY RAND() LIMIT 9;" );
UNTESTED:
SELECT id, #rownum:=#rownum+1 AS rownum, name
FROM users u,
(SELECT #rownum:=0) r
THis will give a unique number to each row in sequence. Now if you create a temp table with 9 random numbers between 1 and count(*) of your table and JOIN those two together...
Not sure about performance but seems like it might be faster than Rand and order by since I only need 9 random numbers

Query to return certain row always

Can you suggest a query where a condition will be executed always . Say :
if($country == 'hong-kong')
{
$cond = "AND country = 'china'";
}
$q = "
SELECT
*
FROM
tbl_people
WHERE
region = 'asia'
$cond
ORDER BY RAND()
LIMIT 0,15
";
Now suppose $country = 'hong-kong' and it has total 4 members . What I want to know is is there any way so that if $country = 'hong-kong' I wll have 15 members in result set including the 4 members from hong-kong ?
If I understand the question correctly, you want 15 random results from a larger set, but you want to be sure that all of the results matching country = 'hong-kong' are included in those 15.
I think you're going to want two queries, because the number of records returned by the first one (country = 'hong-kong') will dictate the limit you put on the second query. E.g.:
SELECT
*
FROM
tbl_people
WHERE
country = 'hong-kong'
ORDER BY RAND()
LIMIT 0,15
Then
SELECT
*
FROM
tbl_people
WHERE
region = 'asia' AND country <> 'hong-kong'
ORDER BY RAND()
LIMIT 0,X
...where X is 15 - N, where N is how many rows were returned by your first query.
Then of course, you have to combine them in some kind of randomizing way (unless you want the Hong Kong results to be at the top/bottom/whatever of the list).

PHP add many rows to database

I have problem with database. I want to add about 10 new rows after pageload. It should check if there is an article with id (that is actually loading). If its not, add 10 of that id with different tag id.
$sprawdz = "SELECT id_artykulow FROM tag_art WHERE id_artykulow='".$_GET['id']."' " ;
$miasto = mysql_query($sprawdz);
$a = mysql_num_rows($miasto);
while ($a<=10){
$zm = "SELECT id FROM tag_content ORDER BY RAND() LIMIT 1";
$sw = mysql_query($zm);
while($row3=mysql_fetch_array($sw)){
$zmienna = "INSERT INTO tag_art(id_artykulow, id_tagow) VALUES ('".$_GET['id']."', '".$row3['id']."' ) ";
$cokolwiek = mysql_query($zmienna);
}
$a++;
}
There is two tables. One tag_content with id (of the tags), and another tag_art with id_artykulow (= id of the article) and id_tagow (id of the tag taken from tag_content)
Don't know why, but it doesn't add 10 rows (it should be for example ten id_artykulow = 10, with different id_tagow). How to fix it?
Thx for help and let me know if u need more informations (like more code etc.)
why do you have 2 while loops?
cant you just replace
while ($a<=10){
$zm = "SELECT id FROM tag_content ORDER BY RAND() LIMIT 1";
with
if ($a<=10){
$zm = "SELECT id FROM tag_content ORDER BY RAND() LIMIT " . (10-$a);
or just let the databse do all the work by:
$query = "INSERT INTO tag_art(id_artykulow, id_tagow) SELECT '".$_GET['id']."', id FROM tag_content ORDER BY RAND() LIMIT " . (10-$a);
(and i would also recomendate protection agains sql-injections, mysql_real_escape_string())
Or let the database do all the work:
// store id as mysql-variable, but let php force it to an integer first
mysql_query("SET #id = " . (int) $_GET['id']);
// calculate how many new art we need, to get 10, using greatest to avoid negative numbers
mysql_query("SELECT #art_needed := GREATEST(0, 10 - COUNT(*)) FROM tag_art WHERE id_artykulow = #id");
// Need to use prepere to be able to have an mysql-variable as limit
mysql_query("PREPARE art_stmt FROM 'INSERT INTO tag_art(id_artykulow, id_tagow) SELECT ?, id FROM tag_content ORDER BY RAND() LIMIT ?'");
// execute the query using the mysql-variables
mysql_query("EXECUTE art_stmt USING #id, #art_needed");
just reread my old answer, and no longer agrees with "or just let the databse do all the work by" for that answer, the database could do alot more.

Categories