I have the following structure:
Table: products
id, name, sort_order
Let's say I only have 5 products, but I want 20.
How would I loop through to get 20 results?
Additionally, I'll need to start at a specific sort_order. So let's say I have
1,2,3,4,5
and I want to get 20 results and start at 3. It should end up as:
3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,etc.
Any ideas? I'm totally lost.. thank you!
$how_many = 20;
$counter = 0;
$rows = array();
$result = mysql_query("Your query");
while($row == mysql_fetch_row($result)) {
$rows[] = $row;
}
$expanded = array();
for($i = 0; $counter < $how_many; $counter++) {
$expanded[] = $rows[$i];
$i++;
if($i == count($rows)) {
$i = 0;
}
}
And now $expanded is filled with those 5 rows for 4 times.
Edit: and don't forget to adjust $i to your start element. For your example that would mean $i = 2 (third element).
create a table named abc and use this procedure to populate the table
CREATE PROCEDURE `populator`(i INT)
BEGIN
DECLARE existing int;
DECLARE counting int;
DECLARE limitation int default i;
SELECT COUNT(*) INTO existing FROM asd;
theLoop: LOOP
SELECT COUNT(*) INTO counting FROM abc;
IF (existing + counting) > i
THEN SET limitation = i - counting;
END IF;
IF counting >= i
THEN LEAVE theLoop;
ELSE
SET #sql = CONCAT("INSERT INTO abc SELECT id from asd LIMIT ", limitation);
PREPARE s1 FROM #sql;
EXECUTE s1;
DEALLOCATE PREPARE s1;
END IF;
END LOOP theLoop;
END
remember to rename the tables. i used asd as source table and abc as destination table. if you do the concatenation thing right you can even write a procedure that works with temporary tables.
You can do this in MySQL by cheating:
SELECT
t1.id
FROM
products t1
CROSS JOIN products t2
LIMIT
2, 20
Like the others, I'm not 100% sure what you want. You want to show the same products over again until 20 times?
Related
I have two queries, one which will get a list of IDs from the main table, and another which will get all records in another table that relate to that ID.
I'd like to split it into pages of results, but my google searches are only coming up with people who want to have a certain number of results per column value, not who want to limit the overall count by it.
The main table has a column that contains the number of records, so actually reading the other table isn't needed. The limit would be used as a minimum value, so if there are still records left in the current group after the limit, it'll continue displaying them. That'd be somehow calculated as part of the offset so it can start in the correct place.
Here's an example of what I mean:
Table 1:
ID | Records
1 | 2
2 | 3
3 | 28
4 | 7
...
Table 2 (contents don't need to be known for this question):
ID | GroupID | Value
1 | 1 | x
2 | 1 | x
3 | 2 | x
4 | 2 | x
5 | 2 | x
6 | 3 | x
...
If the limit was given as 3 for example, both 1 and 2 should display on the first page, since just 1 by itself is under the limit. The next page will then start on 3, and that'll take up the entire page.
I could just manually count up using PHP until I reach the limit, though it might end up going slow if there were a lot of pages (I've no idea if mysql would be any better in that regard though). Here's a quick example of how that'd work to get the offset:
$page = 2;
$limit = 40;
$count = 0;
$current_page = 1;
$query = 'SELECT ID, Records FROM table1';
$stmt = $conn->prepare($query);
$stmt->execute();
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
$count += $row['Records'];
if($count > $limit){
$current_page ++;
$count = 0;
if($current_page == $page){
$start_id = $row['ID'];
break;
}
}
}
Updated:
A "custom" Pagination is what you're looking for. So if you're planning to hop from page to page, you can't use hardcoded $page and $current_page values. Those values should be generated dynamically once you're on a particular page. In fact, you should have the ID column value in the query part of the URL so that the pagination links could satisfy your business logic.
Assuming the fact that your ID column value starts from 1, your code should be like this:
$id = isset($_GET['id']) && is_numeric($_GET['id']) ? $_GET['id'] : 1;
$limit = 40;
// Display query results based on particular ID number
$query = 'SELECT ID, Records FROM table1 WHERE ID >= ' . $id;
$stmt = $conn->prepare($query);
$stmt->execute();
$rowCount = $stmt->rowCount();
if($rowCount){
$total = 0;
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
if($total <= $limit){
$total += $row['Records'];
// Display $row details here
}else{
// Get the next $id value
$id = $row['ID'];
break;
}
}
}
// Display relevant ID links
$query = 'SELECT * FROM table1';
$stmt = $conn->prepare($query);
$stmt->execute();
$idArr = array();
$total = 0;
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
if($total == 0){
$idArr[] = $row['ID'];
}
if($total <= $limit){
$total += $row['Records'];
}else{
$total = 0;
}
}
foreach($idArr as $idValue){
if($idValue == $id){
echo '<a>'.$idValue.'</a> ';
}else{
echo ''.$idValue.' ';
}
}
Sidenote: If the ID column value of your table doesn't start from 1, then use a separate query to get the first ID value and update the following statement,
$id = isset($_GET['id']) && is_numeric($_GET['id']) ? $_GET['id'] : <FIRST_ID_VALUE>;
If you only need to create the "Next" button, you could try this way:
select t1.ID, t1.Records
from table1 t1
left join table1 t2
on t2.ID < t1.ID
and t2.ID > :last_selected_id_1
where t1.ID > :last_selected_id_2
group by t1.ID, t1.Records
having coalesce(sum(t2.Records), 0) < :limit
order by t1.ID
:last_selected_id_x is the last ID from the current page or 0 for the first page.
http://rextester.com/FJRUV28068
You can use MySQL session variables to create page links:
select page, min(ID) as min_id, max(ID) as max_id
from (
select ID
, #page := case when #sum = 0 then #page + 1 else #page end as page
, #sum := case when (#sum + Records) >= :limit
then 0
else #sum + Records
end as sum
from table1
cross join (select #sum := 0, #page := 0) initvars
order by ID
) sub
group by page
The result would look like:
page | min_id | max_id
1 | 1 | 2
2 | 3 | 3
3 | 4 | 4
http://rextester.com/LQVJWP18655
Note that it is officially (as of documentation) not recomended to use the session variables like that (read and write in one statement). Feature versions may break your code.
Here's the result I ended up with, I ended up basing it off Rajdeeps answer but tweaked so that it'll allow sorting.
$query = 'SELECT ID, Records FROM table1 ORDER BY something';
$stmt = $conn->prepare($query);
$stmt->execute();
$id_array = array(); // For storing each ID
$current_ids = array(); // For storing the current ID pages
$initial_ids = array(); // For storing the first page, in case the page is too high
$count = 0;
$current_page = 1;
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
$already_reset = false;
// Move the session to the next page if it's more over the limit than under
// For example, if a limit of 10 has 6 records already, and the next ID is 9 records, then start a new page
if($id_array and $count + $row['Records'] / 2 > $max_links){
array_push($id_array, $last_id);
$current_page ++;
$count = 0;
$already_reset = true; // Set a marker so that if this ID is larger than the limit then don't start a 2nd new page
}
// Backup the first results in case page is too high and needs to be reset to 1
if($current_page == 1){
array_push($initial_ids, $row['ID']);
}
$count += $row['Records'];
// The values that appear here are the results for the selected page
// They may not be in sequence so store the individual IDs
if($_GET['page'] == $current_page){
array_push($current_ids, $row['ID']);
}
// Start a new page if over the limit
if($count > $max_links and !$already_reset){
$current_page ++;
$count = 0;
array_push($id_array, $row['ID']);
}
$last_id = $row['ID'];
}
array_push($id_array, $last_id);
$total_pages = count($id_array);
// If page is invalid revert to default
if(!$current_ids){
$current_ids = $initial_ids;
$_GET['page'] = 1;
}
$_GET['page'] = max(1, min($total_pages, intval($_GET['page'])));
$current_ids is an array of the IDs for the current page, and $total_pages is pretty self explanatory.
Suppose I have a table TABLE:
NAME ID ...
m -1 ...
f -1 ...
g -1 ...
b -1 ...
z -1 ...
And I want to turn it into:
NAME ID ...
f 1 ...
g 2 ...
m 3 ...
b -1 ...
z -1 ...
You probably get the idea:
select the first 3 rows from the original table (preserving order)
order selected rows by the NAME column.
update selected rows' IDs with their position in the new table (keeping the remaining unselected rows in their original positions).
So (m, f, g) got sorted to (f, g, m) and (b, z) remained (b, z).
Here's how I am trying to do it in PHP:
$count = 0;
$query = "UPDATE TABLE SET ID = $count:= $count + 1 ORDER by NAME DESC LIMIT 3";
mysqli_query($con, $query);
But I don't think I can just go ahead and increment a counter and store its value like that. Any advice?
You can try this :
$limit = 3;
for($count = 0 ; $count < $limit;$count++ ){
$query = "UPDATE TABLE SET ID = $count + 1 WHERE ID = '-1' ORDER by NAME DESC";
mysqli_query($con, $query);
}
$query = "UPDATE TABLE SET ID = '-1' WHERE ID > $limit ORDER by NAME DESC";
mysqli_query($con, $query);
In the above logic :
In the final loop, all the IDs are set to $limit
However the update command outisde the loop will set back IDs to -1 again
First, you can quickly query for the first 3 rows in the table and get the name property only and assign the value in an array.
$sql = "select name from table order by name limit 3"
$query = $mysqli->query($sql);
Now let's construct a helper array:
while ($row = $mysqli->fetch_assoc()) {
$a[] = $row['name'];
}
Now just structure the queries:
foreach($a as $id => $name) {
$query = "update table set id={$id+1} where name='$name' limit 1";
// execute the query
}
Note that I assume that the name is unique so I added the limit 1 directive to tell it stop looking for rows to update once it has found a row.
Also, don't forget that array keys are counting starting from 0, hence we are adding 1 to the $id in the loop.
There may be more elegant solutions but this one is rather easy to understand and use.
In MySQL:
SET #row_number = 0;
update TABLE d
join
(
select
NAME,
#row_number:=#row_number+1 as ID,
from
(select NAME from TABLE limit 3) t
order by
NAME asc
) s on s.NAME = d.NAME
set d.ID = s.ID;
SQLFiddle: http://sqlfiddle.com/#!9/dffecf/1
This assumes NAME is your unique key, otherwise likely best to replace with an Identity column in your table and use that for the update.
This approach may require some syntax changes depending on your DB engine. By doing this in SQL, we only make one pass at the DB. Not a huge deal to iterate in multiple passes with PHP if you're only updating three records, but if it was a 1000, etc.
i have 3 tables table_a(4000 rows) and table_b(35000 rows) and table_c to store the result,
it takes 670 sec to complete...., is there another way to do this..?,( i also try left join , but the right table give result more than one, and the left result become more than one, and it takes about 300 sec to complete.....
autocommit = 0
$c_mgp = "select * from table_a where .......";
$c_mgp_r = mysqli_query($con_a,$c_mgp) or die (mysqli_error($con_a));
$multi_sq = '';
$r = 0;
while($c_mgp_f = mysqli_fetch_array($c_mgp_r)) {
$r++;
$mgpstat = trim($c_mgp_f['STATUS']);
$mgpval= trim($c_mgp_f['VAL']);
$sand = trim(($c_mgp_f['SAND']);
$multi_sq .= "insert into table_c (NAME,VAL,VAL_RES) values('$mgpstat','$mgpval',
(select SUM(VAL_RES) from table_b where DATE = '$date_a' and GRUP = '$grup' and ACNO= '$sand'));" //this part is the most important thing, $sand always different (and always more than one row in result) each loop
if($r == 500){
mysqli_multi_query...........;
$r=0;
$multi_sq='';
}
}
commit
many thanks for the help...
I would like get number of records in a table then divide them by 4, after dividing them by 4 i want to create sql statements with limit ranges based on my result. For example I have a table with 8 records I divide by 4, I will create 2 sql statements with a limit range like limit 0,4 and limit 4,8
Final results will look like
Select * from prop where id=123 LIMIT 0,4
Select * from prop where id=123 LIMIT 4,8
My approach was to have for loop which will count the number of sql statements to be made.
Then in the loop: first circle 0-4 and second will be 4-8
Am struggling on the limit 0-4 and limit 4-8
PHP script
include('connect.php');
$query_1 = "Select COUNT(*) as Total from prop where ref = 'SB2004'";
$results_query_1 = mysql_query($query_1);
while($row_query_1 = mysql_fetch_array($results_query_1))
{
$cnt = $row_query_1['Total'];
}
echo $cnt;
echo "<br>";
$num_grps = 0;
if ($cnt % 4 == 0 )
{
echo $num_grps = $cnt / 4 ;
}
$count_chk= $num_grps * 4;
for ($i=1;$i<=$num_grps;$i++)
{
//for loop for range
for()
{
$range = '0,4';
echo "SELECT prop_ref from prop limit".$range;
}
}
Either you've not understood the problem or haven't explained it very well.
The most immediate problem here is that you have misunderstood the syntax for the LIMIT clause. The first argument specifies the offset to start at and the second defines the number of rows to return, hence LIMIT 4,8 will return 8 rows (assuming there are 12 or more rows in the dataset).
The next issue is that you've not said if the results need to be reproducible - e.g. if you have rows with primary keys 1 and 2, should these always be returned in the same query. In the absence of an explicit ORDER BY clause, the rows will be returned based on the order in which they are found by the query.
The next issue is that you've not explained how you want to deal with the last case when the total number of rows is not an even multiple of 4.
The code you've provided counts the number of rows where ref = 'SB2004' but then creates queries which are not filtered - why?
The code you've provided does not change the limit in the queries - why?
The next issue is that there is never a good reason for running SELECT queries inside a loop like this. You didn't exlpain what you intend doing with the select queries. But based on the subsequent update....
include('connect.php');
$query_1 = "Select COUNT(*) as Total from prop where ref = 'SB2004'";
$cnt = mysql_fetch_assoc(mysql_query($query_1));
$blocks=$cnt['Total']/4 + (0 == $cnt['Total'] % 4 ? 0 : 1);
$qry2="SELECT * FROM prop where ref='SB2004' ORDER BY primary_key";
$res=mysql_fetch_assoc($qry2);
for ($x=0; $x<$blocks; $x++) {
print "<div>\n$block<br />\n";
for ($y=0; $y<4; $y++) {
print implode(",", #mysql_fetch_assoc($res)). "\n";
}
print "</div>\n";
}
It's trivial to refine this further to only issue a single query to the database.
If you really must generate individual SELECTs....
include('connect.php');
$query_1 = "Select COUNT(*) as Total from prop where ref = 'SB2004'";
$cnt = mysql_fetch_assoc(mysql_query($query_1));
$blocks=$cnt['Total']/4 + (0 == $cnt['Total'] % 4 ? 0 : 1);
for ($x=0; $x<$blocks; $x++) {
$y=$x*4;
print "SELECT * FROM prop where ref='SB2004'
ORDER BY primary_key LIMIT $y,4<br />\n"
}
I'm trying to show only every 2nd row after selecting something from my DB.
My select looks like this:
SELECT * FROM table where partnerID = '1'
Now from the results of that select query I want to get only every 2nd row.
Is that possible ?
$count = 0;
while($row = mysql_fetch_array($results)) {
$count++;
if($count % 2 == 1) continue;
// What you want to do with the rows you don't want to skip here...
}
On a side note, you can use this same strategy to only show every 3rd row, or every 4th row, simply by changing what number you put next to the modulus operator
if($count % 3 != 0) continue; // Show only every third row
if($count % 4 != 0) continue; // Show only every fourth row
SELECT * FROM (
SELECT #n := #n + 1 AS position,
t.*
FROM (SELECT #n:=0) counter,
table t
WHERE partnerID = 1)
AS query
WHERE MOD(position,2) = 0
You can do this in the following way:
SELECT * FROM
(
SELECT
#I := #I + 1 AS rowNumber,
tablename.*
FROM
tablename,
(SELECT #I := 0) VT1
WHERE partnerID = 2
ORDER BY ID
) NumberedRows
WHERE MOD(rowNumber, 2)=0
;
Not sure if this is the right answer, but it looks like you would need to use something like MOD function (http://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html#function_mod)
SELECT * FROM `table` WHERE id & 1;
Would return odd rows