Binary search in a MySQL table with deleted rows PHP - mysql

I have a large MySQL table with sorted data. When I need to find a starting point, I perform a binary search to find the lower bound ID (auto increment). The only problem is once some data is deleted, I need to look at the first existing row with a lower ID if the ID given by the algorithm doesn't exist. How should I modify this code to achieve that?
$l = 1;
$h = $max; //SELECT MAX(id)
while ($h - $l > 1){
$m = ($h + $l) / 2;
$q = mysqli_query($db, "SELECT col FROM tab WHERE id=". floor($m));
$result = array();
while($result[] = mysqli_fetch_row($q)){;}
if ($result[0][0] < $val) $l = $m;
else $h = $m;
}
echo round($m);
For example I want to find which rows have the value of col greater than 12345 and the table has max ID 10000. I start by looking at row 5000, where the col = 9000, then 7500 (col = 13000), then 6250 has been deleted, so I start looking for the 1st existing row with ID < 6250 and I find that 6245 has col = 10500. Now I'm looking between IDs 6873 and 7500 etc.

The right way to do this
So you have a table like this:
| ID | col |
---------------
| 1 | 15 |
| 3 | 155 |
| 18 | 9231|
| 190 |14343|
| 500 |16888|
You can get find 14343 with the following query:
SELECT ID, col FROM the_table WHERE col>12345 LIMIT 1;
To make it faster, you'd need to add an index (index word is worth googling)
ALTER TABLE `the_table` ADD INDEX `col` (`col`);
After that mysql will create a tree structure internally and will be doing binary searches on it for you.
This will be working much faster as you'll avoid multiple network roundtrips + other per request expenses (query parsing, optimization, all the locks & mutexes, ...)
Answer to your question
I need to look at the first existing row with a lower ID
E.g. you'd like to get first row with an ID < than 300, you do this (limit is what makes the query return only 1 result):
SELECT col FROM the_table WHERE ID < 300 LIMIT 1;

Related

How with mysql get the same as with php insert...on duplicate key update multiple rows?

For example have such table (named purchase_invoice_items)
Id
NameOfItem
PurchaseQuantity
PurchaseDate
SoldQuantity
1
x
2
2022-04-01
2
y
11
2022-04-01
3
z
8
2022-05-19
4
x
23
2022-08-19
5
x
15
2022-05-19
And i know that sum of sold quantity for NameOfItem x is 20. Sold 20 units of item x. I want to distribute the sold items between PurchaseQuantity using first-in-first-out method. Want to see table like this
Id
NameOfItem
PurchaseQuantity
PurchaseDate
SoldQuantity
1
x
2
2022-04-01
2
2
y
11
2022-04-01
3
z
8
2022-05-19
4
x
23
2022-08-19
3
5
x
15
2022-05-19
15
Using mysql two queries and php, i can do it in following way.
At first i select necessary data from mysql:
$sql_select_purchase_data = 'SELECT `IdPii`, `PurchasedQuantity`
FROM `purchase_invoice_items` WHERE `NameOfItem` = "x"
ORDER BY `PurchaseDate` ASC;';
Then create sql to update.
$sql_update_sold_quantity = 'INSERT INTO `purchase_invoice_items` (`IdPii`, `SoldQuantity`) VALUES ';
php code to continue creating sql
if( isset($arr_select_purchase_data) ){
$sum_of_sold_quantity = 20;
foreach( $arr_select_purchase_data as $one_arr_select_purchase_data ){
if( $sum_of_sold_quantity > 0 ){
$sql_update_sold_quantity .= '(?,?), ';
$data_update_sold_quantity[] = $one_arr_select_purchase_data['IdPii'];//For 'IdPii'
$data_update_sold_quantity[] = min( $one_arr_select_purchase_data['PurchasedQuantity'], $sum_of_sold_quantity);//For 'SoldQuantity'
$sum_of_sold_quantity = $sum_of_sold_quantity - min( $one_arr_select_purchase_data['PurchasedQuantity'], $sum_of_sold_quantity);
}//if( $sum_of_sold_quantity > 0 ){
else{ break; }
}//foreach(
$sql_update_sold_quantity = rtrim(trim($sql_update_sold_quantity), ','). ' ON DUPLICATE KEY UPDATE `SoldQuantity`= VALUES(`SoldQuantity`);';
But this is waste of resources (if i need to select-update many rows)? Two mysql queries and additionally php code.
Any ideas how can i get the same using only mysql (one mysql query; without php)?

How do I count multiple columns with a vertical value?

I'm new to programing. I have table
check_1,check_2,check_3 ..etc
------------------------------
1 1 1
0 0 1
1 0 1
And I want this output :
column_name, count_true
-----------------------
check_1 2
check_2 1
check_3 3
I've tried it with mysql using the union function, but when I try to apply it in laravel I have trouble with union. Is there a unionless query that can produce such output?
Thanks in advance
You can do this way. One query in db
$records = DB::table('your_table')->get();
$check1Count = $records->where('check_1', 1)->count();
$check2Count = $records->where('check_2', 1)->count();
$check3Count = $records->where('check_3', 1)->count();
......
Or
$records = DB::table('your_table')->get();
$columns = ['check_1', 'check_2', 'check_3', ...];
$data = [];
foreach($columns as $column) {
$data[] = [
'column_name' => $column,
'count_true' => $records->where($column, 1)->count();
];
}
Also you can do this way but it is many query
$check1Count = DB::table('your_table')
->selectRaw('COUNT(check_1) as count')
->where('check_1', 1)
->first()
->count;
$check2Count = DB::table('your_table')
->selectRaw('COUNT(check_2) as count')
->where('check_2', 1)
->first()
->count;
.....
A normalised approach might look like this:
response_id checkbox
1 1
1 2
1 3
2 3
3 1
3 3
You don't need union, just query all the data and process it on Laravel. Let say you query all the data using eloquent to $data variable, then you should do it like this:
//preparing a variable to hold all 24 database field value
$total = [];
foreach ($data as $d) {
//do this for all 24 database field
if ($d->check_1) {
$total[1]++;
}
if ($d->check_2) {
$total[2]++;
}
...
}
By using that way you can't check the resutl on $total[index] variable. And yes, there is a better way to store your data instead of saving all the field for each user. You can just store all checked value in database that look like this :
user_id checkbox_id
1 3
1 5
1 9
1 24
2 23
3 2
3 3
It more efficient since you don't need to save the unchecked checkbox value, if they more likely not to checked most of the checkbox.

how do I get Add and Sum by column index value in Google Spreasheet

I have a sheet, say it's called raw sheet like this:
Column Index | A | B |
David | 1 | 10 |
Jerry | 5 | 15 |
David | 1 | 50 |
Jerry | 6 | 20 |
David | 8 | 20 |
There are only limited values in Column Index. Like in this case only "David" and "Jerry".
I want to create another sheet, say it's called summary sheet that can summarize some value by the Column Index value, like this:
Column Index Summary | f(A,B) |
David | some value |
Jerry | some value |
The f(A,B) can be any kind of function that take use of all the values in the first sheet. One example: to add every row's A*B to get a new number. In this case, it would be:
Column Index Summary | f(A,B) |
David | 220 | that is 1*10 + 1*50 + 8*20
Jerry | 195 | that is 5*15 + 6*20
What should I do?
Here's a Google Sheets custom function for you. It will operate on any arbitrary table of numerical data, aggregating the numbers by any arbitrary (but simple) algebraic expression. (Expression must be valid in Javascript, e.g. "A * B", "A + B / C", or even "Math.pow(A,B)".) There's no error checking, so it's not fool-proof.
Examples:
=summary('raw sheet'!A1:C6,"A*B") Yes, you can refer to different sheets.
=summary(A1:C6,"A*A + B")
=summary(A1:C6,"Math.pow(A,B)")
Custom Function
/**
* Performs given formula on each row in table, aggregating (summing)
* row results by the key value in first column.
*
* See: http://stackoverflow.com/questions/26925283/how-do-i-get-add-and-sum-by-column-index-value-in-google-spreasheet/26942156#26942156
*
* #param {range} table Input data table, including headers
* #param {string} formula Mathematical function to peform on each
* row in table, using header values as
* function parameters.
* #param {int} sortType (Optional, default 1) 0: do not sort, 1: sort ascending, -1: sort descending
* #param {int} sortColumn (Optional, default 1) Column to sort by.
*
* #return {range} Summary table of results
* #customfunction
*/
function summary(table,formula,sortType,sortColumn) {
sortType = (sortType == undefined) ? 1 : sortType;
sortColumn = (sortColumn == undefined) ? 1 : sortColumn;
// Sort comparison function for ordering summary table
// uses sortType & sortColumn
function colCompare(a,b)
{
var col = sortColumn - 1;
var order = sortType;
if (!order) return 1;
else
return ((a[col] < b[col]) ? -order : ((a[col] > b[col]) ? order : 0));
}
var headers = table[0];
// Start results with its header row
var summaryTable = [[headers[0],String(formula)]];
// evaluate formula, replacing variables (headers) with references to table
for (var h = 1; h < headers.length; h++) {
var re = new RegExp(headers[h],"g");
formula = formula.replace( re, " table[row]["+parseInt(h)+"] " );
}
// Aggregate data by summing formula for each row
var summary = {};
for (var row=1; row<table.length; row++) {
var key = table[row][0];
if (!(key in summary))
summary[key] = 0;
summary[key] += eval( formula );
}
// Append aggregated rows to results, and return
for (key in summary) {
summaryTable.push([key,summary[key]]);
}
// Sort the results
headers = summaryTable.splice(0, 1);
summaryTable.sort(colCompare).unshift(headers[0]);
return summaryTable;
}
EDIT: Nov 17 - added sort functionality
Assuming you have the names in Col A and the col A of the example is actually Col B,
in the summary sheet, try something like:
=ArrayFormula(query({'raw sheet'!A2:A,'raw sheet'!B2:B*'raw sheet'!C2:C}, "select Col1, sum(Col2) where Col1 <>'' group By Col1 label sum(Col2) 'TOTAL' "))

php script to move data between columns with mysql

What I want to achieve is to move the data between 2 rows within one table.
Column A
--------
FN2
1 200x310mm
2 400x260mm[+0.84]
3 500x500mm[+11.34]
Column B
--------
0.0000
0.0000
0.0000
0.0000
This is how it should look like after the data move:
Column A
--------
FN2
1 200x310mm
2 400x260mm
3 500x500mm
Column B
--------
0.0000
0.0000
+0.84
+11.34
What I want is that the query between the [ ] is moved to column B and replaces the 0.0000
How can I achieve this?
Kind Regards
just to illustrate what Yadav said
$query = "SELECT columnID, columnA FROM table";
$result = mysql_query($query,$conn);
while ($row = mysql_fetch_array($result)){
$id = $row['columnID'];
$a = $row['columnA'];
$pos1 = strpos($a,"[")+1;
$pos2 = strpos($a,"]");
$b = substr($a,$pos1,$pos2-$pos1);
$query = "UPDATE table SET columnB = $b WHERE columnID = $id";
mysql_query($query,$conn);
}//end while
edit: Yadav obviously proposed a better answer while I was typing mine...
try this it works for you here i used id as unique key ... and test is my database
<?php
$con=mysql_connect("localhost","root","");
$db=mysql_select_db("test");
$query=mysql_query("SELECT * FROM test where columnA LIKE '%[%]'");
while($row=mysql_fetch_assoc($query))
{
if(preg_match_all('/\[(.*?)\]/',$row['columnA'],$match))
{
mysql_query("UPDATE test SET columnB='".$match[1][0]."' WHERE id=".$row['id']."");
}
}
?>

Weighted rotator table design

I am having a hard time with this very simple project. Maybe it's because it's Monday, I'm not sure.
I have a table that looks like this:
id | weight | hits
------------------------------------------------
1 | 4 | 0
2 | 1 | 0
3 | 2 | 0
Obviously, the hits column will increment by 1 each time that particular record is selected (We will run this update from within our PHP script).
How can I best retrieve these records by their weight? What I mean is that if I ran my query 15 times, I would want the following IDs returned:
1
1
1
1
3
3
2
1
1
1
1
3
3
2
1
We have a simple formula in place that we got online that retrieves a random weighted result, but we aren't running the formula enough to statistically balance out the results, so instead we need to do a simple rotation as described above.
I know that this is a simple problem to solve, but I'm having a hard time coming up with the best way to do it today.
I hope I've been clear enough in my description of the problem.
Unless I'm missing something (it is Monday here too after all), can't you just sort by hits / weight and LIMIT 1?
Run:
SELECT id FROM tbl WHERE weight - hits > 0 ORDER BY weight DESC LIMIT 0,1
With the last result, run:
UPDATE tbl SET hits = hits + 1, total_hits = total_hits + 1 WHERE id = (THE RESULT);
Then run
SELECT (Sum(weight-hits)) FROM tbl
If result = 0
UPDATE tbl SET hits = 0
EDIT:
It's possible to make it with two queries:
$query="SELECT id FROM tbl, weight - hits AS diff WHERE weight - hits > 0 ORDER BY weight DESC";
$result=mysql_query($query);
$num=mysql_numrows($result);
$i=0;
$id=mysql_result($result,$i,"id");
$diff =0;
while ($i < $num) {
$diff= $diff + mysql_result($result,$i,"diff");
++$i;
}
if( $diff = 1 and $i = 1);
$query2="UPDATE tbl SET hits = 0, total_hits = total_hits + IF(id='$id',1,0)";
else{
$query2="UPDATE tbl SET hits = hits + 1, total_hits = total_hits + 1 WHERE id = '$id'";
}
mysql_query($query2);