Let's put an easy example with two tables:
USERS (Id, Name, City)
PLAYERS (Id_Player, Number, Team)
And I have to do a query with a subselect in a loop, where the subselect is always the same, so I would like to divide it into two queries and put the subselect outside the loop.
I explain. What works but it is not optimize:
for($i=0;$i<something;$i++)
{
$res2=mysql_query("SELECT Team from PLAYERS WHERE Number=$i
AND Id_Player IN (SELECT Id FROM USERS WHERE City='London')");
}
What I would like to do but it doesn't work:
$res1=mysql_query("SELECT Id from USERS where City='London'");
for($i=0;$i<something;$i++)
{
$res2=mysql_query("SELECT Team from PLAYERS WHERE Number=$i
AND Id_Player IN **$res1**");
}
Thanks!
Something like this should work.
<?
$sql = "SELECT Team from PLAYERS
JOIN USERS on (Id_player=Id)
WHERE Number BETWEEN $minID AND $maxID
AND City='London'
GROUP BY Team";
$results=mysql_query($sql) or die(mysql_error());
// $results contain all the teams from London
// Use like normal..
echo "<ul>\n";
while($team = mysql_fetch_array($results)){
echo "\t<li>{$team['Team']}</li>\n";
}
echo "</ul>";
Placing SQL quires in loops can be very slow and take up a lot of resources, have a look at using JOIN in you SQL. It's not that difficult and once you've got the hang of it you can write some really fast powerful SQL.
Here is a good tutorial worth having a look at about the different types of JOINs:
http://www.keithjbrown.co.uk/vworks/mysql/mysql_p5.php
SELECT PLAYERS.*, USERS.City FROM PLAYERS, USERS WHERE USERS.City='London' AND PLAYERS.Number = $i
Not the best way to do it; maybe a LEFT JOIN, but it should work. Might have the syntax wrong though.
James
EDIT
WARNING: This is not the most ideal solution. Please give me a more specific query and I can sort out a join query for you.
Taking your comment into account, let's take a look at another example. This will use PHP to make a list we can use with the MySQL IN keyword.
First, make your query:
$res1 = mysql_query("SELECT Id from USERS where City='London'");
Then, loop through your query and put each Id field one after another in a comma seperated list:
$player_ids = "";
while($row = mysql_fetch_array($res1))
{
$player_ids .= $row['Id'] . ",";
}
$player_ids = rtrim($player_ids, ",");
You should now have a list of IDs like this:
12, 14, 6, 4, 3, 15, ...
Now to put it into your second query:
for($i = 0; $i<something; $i++)
{
$res2 = mysql_query("SELECT Team from PLAYERS WHERE Number=$i
AND Id_Player IN $player_ids");
}
The example given here can be improved for it's specific purpose, however I'm trying to keep it as open as possible.
If you want to make a list of strings, not IDs or other numbers, modify the first while loop, replacing the line inside it with
$player_ids .= "'" . $row['Id'] . "',";
If you could give me your actual query you use, I can come up with something better; as I said above, this is a more generic way of doing things, not necessarily the best.
Running query in a loop is not a great idea. Much better would be to get whole table, and then iterate through table in loop.
So query would be something like that:
"SELECT Team from PLAYERS WHERE Number BETWEEN($id, $something)
AND Id_Player IN (SELECT Id FROM USERS WHERE City='London')"
$res1=mysql_query("SELECT Id from USERS where City='London'");
for($i=0;$i<something;$i++)
{
$res2=mysql_query("SELECT Team from PLAYERS WHERE Number=$i
AND Id_Player IN **$res1**");
}
Would work, but mysql_query() returns a RESULT HANDLE. It does not return the id value. Any select query, no matter how many, or few, rows it returns, returns a result statement, not a value. You first have to fetch the row using one of the mysql_fetch...() calls, which returns that row, from which you can then extract the id value. so...
$stmt = mysql_query("select ID ...");
if ($stmt === FALSE) {
die(msyql_error());
}
if ($stmt->num_rows > 0) {
$ids = array();
while($row = mysql_fetch_assoc($stmt)) {
$ids[] = $row['id']
}
$ids = implode(',', $ids)
$stmt = mysql_query("select TEAM from ... where Id_player IN ($ids)");
.... more fetching/processing here ...
}
Related
I have several id's in a table called "leaderboards" that belong to different users. They're named as:"id_user" and they're not in order. What I want to do is printing divs in a leaderbord which should contain some info that I get from those id_user's.
The only problem I have about it is that after a research on stackoverflow and other websites, I still couldn't find how to select those id_user's in descending order AND be able to take one by one to get the info from that user and then continue with the next id_user, and so on.
I don't know how to select the specific row of each id_user in descending order to do the other codes that I already know how to do.
I hope it's not a duplicate of any other previosly asked question on this website (I really did a research and I couldn't find any specific answer to this question, for the sql part and the php part all together).
Thank you so so much beforehand.
An INNER JOIN between your tables will achieve what you intend.
SELECT *
FROM users
JOIN leaderboards WHERE users.id = leaderboards.id_user
ORDER BY users.id DESC
In each returned row, you will get the columns from both your users and leaderboards tables, so loop over the result and echo the information from the user you need.
$query = 'SELECT...';
$res = mysqli_query($query);
while ($row = mysqli_fetch_assoc($res)) {
echo '<div>'.$row['id'].' - '.$row['username'].' - '.$row['image'].'</div>';
}
You could do with a good read up on both PHP and MySql but I'll give you a clue.
EDIT
$query = "SELECT * FROM `the_name_of_your_table` ORDER BY `user_id` DESC;";
if ($result = mysqli_query($link, $query)) {
/* fetch associative array */
while ($row = mysqli_fetch_assoc($result)) {
print $row["user_id"] . " - " . $row["username"] . "<BR>";
}
/* free result set */
mysqli_free_result($result);
}
I'm looking for a more efficient way of loading a mutliselect list this theory could apply to many situations. The way I do it causes many queries and I was wondering if there is a way to do it in two db queries.
DB Table : users: fields= (userID, name)
data: 1, Dan
data: 2, Cory
data: 3, Matt
DB Table : users_selected: fields(userSelectedID, userID)
data: 1,1
Now with php I normally do the following:
<?php
$q = 'SELECT userID,name FROM users';
$r = mysql_query($r);
echo '<select id="assignToID" name="assignToID[]" multiple="assignToID">';
while($e = mysql_fetch_array($r){
//this is where you have to query the db each time... theres got to be a better way
$q2 = 'SELECT userSelectedID FROM user_selected WHERE userID = "'.$e['userID'].'" ';
$r2 = mysql_query($q2);
$nr = mysql_num_rows($r2);
if($nr>0){
$sel = ' selected="selected" ';
}
else{
$sel = '';
}
echo '<option '.$sel.' value="'.$e['userID'].'"'>'.$e['name'].'</option>';
}
echo '</select>'
?>
So above we have to query for every user we have in the DB in our loop. Is there a way using PHP array_merge or someway to just do one query for all the users then append weather they are selected or not... Just trying to find a more elegant and efficient solution.
Many Thanks
Yes, there is such a way, to get what you need in 1 SQL Query.
SELECT * FROM users
LEFT OUTER JOIN user_selected
ON users.userID = user_selected.userID
This LEFT OUTER JOIN, should and will produce a complete set of recors from table users, witht he matching records (if available) from user_selected. If there is no match, the right side will just contain null.
This works:
$sql = "SELECT id
FROM `users`
WHERE `account_status` = '" . $i . "'";
$query = $this->db->query($sql);
var_dump($query->num_rows());
But this doesn't:
$sql = "SELECT COUNT(*)
FROM `users`
WHERE `account_status` = '" . $i . "'";
$query = $this->db->query($sql);
var_dump($query->num_rows());
How to do a num_rows on a COUNT(*) query? Also is doing it the 2nd way any better performance wise?
Doing a COUNT(*) will only give you a singular row containing the number of rows and not the results themselves.
To access COUNT(*) you would need to do
$result = $query->row_array();
$count = $result['COUNT(*)'];
The second option performs much better since it does not need to return a dataset to PHP but instead just a count and therefore is much more optimized.
In CI it's really simple actually, all you need is
$this->db->where('account_status', $i);
$num_rows = $this->db->count_all_results('users');
var_dump($num_rows); // prints the number of rows in table users with account status $i
$query->num_rows()
The number of rows returned by the query. Note: In this example, $query is the variable that the query result object is assigned to:
$query = $this->db->query('SELECT * FROM my_table');
echo $query->num_rows();
num_rows on your COUNT() query will literally ALWAYS be 1. It is an aggregate function without a GROUP BY clause, so all rows are grouped together into one. If you want the value of the count, you should give it an identifier SELECT COUNT(*) as myCount ..., then use your normal method of accessing a result (the first, only result) and get it's 'myCount' property.
As per CI Docs we can use the following,
$this->db->where('account_status', $i); // OTHER CONDITIONS IF ANY
$this->db->from('account_status'); //TABLE NAME
echo $this->db->count_all_results();
If we want to get total rows in the table without any condition, simple use
echo $this->db->count_all_results('table_name'); // returns total_rows presented in the table
it's my way of solving the above given question
model
$this->db->select('count(id) as ids');
$this->db->where('id', $id);
$this->db->from('your_table_name');
thanks
This will only return 1 row, because you're just selecting a COUNT(). you will use mysql_num_rows() on the $query in this case.
If you want to get a count of each of the ID's, add GROUP BY id to the end of the string.
Performance-wise, don't ever ever ever use * in your queries. If there is 100 unique fields in a table and you want to get them all, you write out all 100, not *. This is because * has to recalculate how many fields it has to go, every single time it grabs a field, which takes a lot more time to call.
I'd suggest instead of doing another query with the same parameters just immediately running a SELECT FOUND_ROWS()
$list_data = $this->Estimate_items_model->get_details(array("estimate_id" => $id))->result();
$result = array();
$counter = 0;
$templateProcessor->cloneRow('Title', count($list_data));
foreach($list_data as $row) {
$counter++;
$templateProcessor->setValue('Title#'.$counter, $row->title);
$templateProcessor->setValue('Description#'.$counter, $row->description);
$type = $row->unit_type ? $row->unit_type : "";
$templateProcessor->setValue('Quantity#'.$counter, to_decimal_format($row->quantity) . " " . $type);
$templateProcessor->setValue('Rate#'.$counter, to_currency($row->rate, $row->currency_symbol));
$templateProcessor->setValue('Total#'.$counter, to_currency($row->total, $row->currency_symbol));
}
my foundation on SQL is pretty weak so I hope you could bear with me. I have three tables: contents, categories, and categorization. The setup was chosen since some content will belong to one or more categories.
I want to fetch contents and its corresponding categories.
This is an overly-simplified version of the current script, without error-checking routines:
$q = "SELECT * FROM contents WHERE contents.foo = 'bar'"
$resource = mysql_query($q);
$categoryFilter = array();
$q2 = "SELECT * FROM categorization WHERE ";
while($content = mysql_fetch_assoc($resource))
{
$categoryFilter[] = "content_id='" . $content["id"] . "'";
}
if(count($categoryFilter))
{
$q2 .= implode(" OR ", $categoryFilter);
mysql_query($q2);
}
That's the gist of it. I hope you get what I am trying to do. I don't know if I can actually use JOINS the content_id may be present in multiple rows in categorization. So what I did was to simply append multiple OR's, trying to fetch items one by one. I really would not like to use multiple queries in this scenario. I hope anyone could suggest an approach
Thanks for your time
One query should be enough to fetch data from all three tables:
SELECT categories.category_id #, other fields
FROM contents
INNER JOIN categorization ON contents.content_id = categorization.content_id
INNER JOIN categories ON categorization.category_id = categories.category_id
WHERE contents.content_id = 1 # AND other filters
Tweak the columns in the SELECT clause and/or conditions in WHERE clause according to your needs.
This should do the same thing as in your example:
$q = "
SELECT *
FROM
contents c
categorization ctg ON ctg.content_id = c.id
WHERE c.foo = 'bar'
";
$result = mysql_query($q);
If I understand it correctly, you can do this in one sql statement
SELECT *
FROM contents t1
JOIN categorization t2
WHERE t1.content_id = t2.content_id AND t1.foo = 'bar'
Also ensure that content_id is indexed both in 'content' and 'categorization'. You may find it worthwhile indexing 'foo' aswell, but it depends on how you are actually searching.
I'm doing this project for university, which is basically a movie database and for a couple of queries I need to know how many rows were selected. For now, there's 2 situations where I need this:
Display a single movie information. I want the count of selected rows to know if the database contains the selected movie by the user. Or is there a better solution for this?
That selected movie has genres, I need to know how many so that I can construct a string with the genres separated by | without adding one to the end of the string.
With MySQL this is easy, I just query the database and use mysql_num_rows() but oci_num_rows() doesn't work quite the same for the SELECT statement.
The only solution I found with OCI/PHP is this:
if(is_numeric($mid) && $mid > 0) {
$stid = oci_parse($db,
'SELECT COUNT(*) AS NUM_ROWS
FROM movies
WHERE mid = '.$mid
);
oci_define_by_name($stid, 'NUM_ROWS', $num_rows);
oci_execute($stid);
oci_fetch($stid);
if($num_rows > 0) {
$stid = oci_parse($db,
'SELECT title, year, synopsis, poster_url
FROM movies
WHERE mid = '.$mid
);
oci_execute($stid);
$info = oci_fetch_assoc($stid);
$stid = oci_parse($db,
'SELECT COUNT(*) AS NUM_ROWS
FROM genres g, movies_genres mg
WHERE mg.mid = '.$mid.' AND g.gid = mg.gid'
);
oci_define_by_name($stid, 'NUM_ROWS', $num_rows);
oci_execute($stid);
oci_fetch($stid);
$stid = oci_parse($db,
'SELECT g.name AS genre
FROM genres g, movies_genres mg
WHERE mg.mid = '.$mid.' AND g.gid = mg.gid');
oci_execute($stid);
$genres_list = null;
while($row = oci_fetch_assoc($stid)) {
$genres_list .= $row['GENRE'];
if($num_rows > 1) {
$genres_list .= ' | ';
$num_rows--;
}
}
$Template->assignReferences(array(
'Index:LinkURI' => $link_uri,
'Movie:Title' => $info['TITLE'],
'Movie:Year' => $info['YEAR'],
'Movie:GenresList' => $genres_list,
'Movie:Synopsis' => $info['SYNOPSIS'],
'Movie:PosterURL' => $info['POSTER_URL'] // FIX: Handle empty poster link
));
$Template->renderTemplate('movieinfo');
} else {
// TODO: How to handle this error?
}
} else {
// TODO: How to handle this error?
}
But I don't like it. I always need to make 2 queries to count the rows and then select the actual data and there's too many lines of code just to count the rows.
This code doesn't show it (haven't done it yet cause I'm looking for a better solution) but I'll also need to do the same for the movie directors/writers.
Is there any better and simpler solution to accomplish this or this is the only way?
I could add separators in the fetch loop until it finishes and then use PHP functions to trim the last separator from the string, but for this project I'm forced to use SEQUENCES, VIEWS, FUNCTIONS, PROCEDURES and TRIGGERS. Do any of these help solving my problem?
I know what SEQUENCES are, I'm using them already but I don't see how can they help.
For VIEWS, they probably wouldn't simplify the code that much (it's basically a stored query right?). For FUNCTIONS, PROCEDURES and TRIGGERS, as far as I understand them, I can't see how can they be of any help either.
Solutions?
Why do the initial count at all? Just issue your select for the movie title. If it's not found, do your error processing. If it is, continue on! If you really need the count, use an analytic to add the count to your query:
'SELECT title, year, synopsis, poster_url
, COUNT(*) OVER (PARTITION BY mid) mcount
FROM movies
WHERE mid = '.$mid
The same goes for your genre selection.
EDIT:
Oracle documentation on Analytic Functions link. I found that analytics are a bit difficult to get from the Oracle docs. Analytic functions by Example by Shouvik Basu provides some simple guidance as to how to use them and helped me quite a bit.
You can try this
$conn = oci_pconnect('user', 'password', 'connectionstring');
$resource = oci_parse($conn, $sql);
oci_execute($resource, OCI_DEFAULT);
$results=array();
$numrows = oci_fetch_all($resource, $results, null, null, OCI_FETCHSTATEMENT_BY_ROW);
Cheers
you can use the ocirowcount it should behave just like mysql_num_rows when making a select
oci_num_rows() Will give you the total row count if executed AFTER oci_fetch_array()