PHP Join multiple tables not working as expected - php

I'll explain briefly what I've done so far-
First of all I have multiple of stores and each store has its own table in which it contains id,item,qty and price and there is a main table called all_items that contains the total qty taken from each store table.
Now each store table either have same item number or not. Here you can see what the tables looks like :
Store tables called : S1, S2, S3
S1
id item qty price
1 x1 10 12
2 x2 10 15
3 x3 5 5
S2
id item qty price
1 x1 10 12
2 x4 6 6
S3
id item qty price
1 x3 1 5
2 x6 5 5
3 x7 5 12
all_items - Contains original total qty
id item qty price
1 x1 20 12
2 x2 10 15
3 x3 6 5
4 x4 6 6
5 x6 5 5
6 x7 5 12
Okay now, I've decided to find out the qty available for each item in all stores for comparing purposes- As follows :
item price S1 S2 S3
x1 12 10 10 -
x2 15 10 - -
x3 5 5 - 5
x4 6 - 6 -
x6 5 - - 5
x7 12 - - 5
I hope its clear for you now. I've created a form with checkbox input type ( for each store ) to allow user select which stores he wants to compare, so after submitting the form - a PHP page contains the following code :
$allstore = $_POST['store']; //Collects name from checkbox ticks under form
function createSelect($allstore)
{
if (empty($allstore))
return "";
$querySelect = "";
$queryJoin = "";
$baseTable = "";
foreach ($allstore as $store => $value) {
if (!$querySelect) {
$baseTable = "all_items";
$querySelect = "SELECT " . $store . ".item_no, " . $store . ".actual_price, " . $store . ".selling_price, " . $store . ".qty as " . $store;
} else {
$querySelect .= ", " . $store . ".qty as " . $store;
$queryJoin .= "
INNER JOIN " . $store . " ON " . $baseTable . ".item_no = " . $store . ".item_no";
}
}
$querySelect .= " FROM " . $baseTable;
$query = $querySelect . $queryJoin;
return $query;
}
$allstore = array(); // The below code allows function to know how many stores selected in $allstore
if (!empty($_POST['store'])) {
foreach ($_POST['store'] as $value) {
$allstore["s_".$value] = 0; // or 1, it doesn't matter because your function adds all the keys
}
}
var_dump(createSelect($allstore)); // Output SQL
$query = (createSelect($allstore));
$result = mysql_query($query);
//Rest of the code .....
Now if you notice $baseTable = "all_items"; makes the whole query fail. However if I change it value to $baseTable = $store; it works and shows an output but not as expected because it turns out S1 is the main now and the result will be totally different because it relays on the S1 items only.
If you can provide any helpful solution will be appreciated.

Why not use UNION?
function createSelect($stores)
{
$query = "";
$baseTable = "all_items";
foreach($stores as $i => $store)
{
$query .= "(SELECT {$store}.id AS {$store}_id, {$store}.item AS {$store}_item, {$store}.price AS {$store}_price, {$store}.qty AS {$store}_qty, '{$store}' as Source FROM {$store}) UNION ";
}
$query .= "(SELECT all_items.id AS {$baseTable}_id, {$baseTable}.item AS {$baseTable}_item, {$baseTable}.price AS {$baseTable}_price, {$baseTable}.qty AS {$baseTable}_qty, '{$baseTable}' as Source FROM {$baseTable})";
return $query;
}
$result = mysql_query(createSelect($allStore));
//Rest of code
//if my tables are S1, S1, and my $baseTable is bs
//echo createSelect(array('S1', 'S2'); will output (SELECT S1.id AS S1_id, S1.item AS S1_item, S1.price AS S1_price, S1.qty AS S1_qty, 'S1' as Source FROM S1) UNION (SELECT S2.id AS S2_id, S2.item AS S2_item, S2.price AS S2_price, S2.qty AS S2_qty, 'S2' as Source FROM S2) UNION (SELECT all_items.id AS all_items_id, all_items.item AS all_items_item, all_items.price AS all_items_price, all_items.qty AS all_items_qty, 'all_items' as Source FROM all_items)

Related

PHP select multiple Array tables

Is it possible to SELECT with multiple Array tables. I know it sounds confusing but here is what I have done :
First of all, I've created a form, that has two checkboxes option as follows :
<form action="something.php" method="post">
<input type="checkbox" name="store[]" value="M1">
<input type="checkbox" name="store[]" value="M2">
<input type="submit" value="Go">
</form>
Now after submitting the form, I can view which store selected by doing foreach loop:
$allstore = $_POST['store'];
foreach ($allstore as $store=>$value) {
echo $value;
}
Everything till now works as needed !
However those two values in checkboxes are considered to be table names ! Now how can I find a way to let PHP select either one or two tables based on user selection $value ?
$query = "SELECT * from **{$allstore[0]},{$allstore[1]}** WHERE .....";
As you can see {$allstore[0]},{$allstore[1]} should be created based under foreach loop. I can't seem to find a way of doing it! Can I insert a function to do it for me?
Like this : $query = "SELECT * from ((( Function Here ))) WHERE .....";
If you have a different way of doing it, Please share it.
Edit :
M1 Table
id |item_no |qty |price
1 x1 10 20
2 x2 5 22
3 x3 3 5
M2 Table
id |item_no |qty |price
1 x1 11 20
2 x9 5 30
3 x10 6 26
The output table should be
item_no | price | M1 | M2
x1 20 10 11
x2 22 5 N/A
x3 5 3 N/A
x9 30 N/A 5
x10 26 N/A 6
That's what I am aiming for. I hope it can be solved !
here's the structure for 2 tables sqlfiddle
I think you can add more tables from here.
SELECT T1.item_no,
COALESCE(M1.price,M2.price) as price,
M1.qty as M1,
M2.qty as M2
FROM
(SELECT item_no FROM M1
UNION
SELECT item_no FROM M2
)T1
LEFT JOIN M1 ON T1.item_no = M1.item_no
LEFT JOIN M2 ON T1.item_no = M2.item_no
UPDATED: I am not too familiar with PHP but I looked up some syntax and was able to dynamically generate SQL based on array of either ["M1","M2"] or ["M1"] or ["M2"]
DynamicPHPtobuildSQL
<?php
//Enter your code here, enjoy!
$allstore = ["M2"];
$item = 0;
$sqlpart1 = "";
$sqlpart2 = "";
$sqlpart3 = "";
$sqlpart4 = "";
foreach ($allstore as $store=>$value) {
$item += 1;
if ($item > 1){
$sqlpart1 .= ",";
$sqlpart2 .= ",";
$sqlpart3 .= " UNION ";
}
$sqlpart1 .= $value . ".price ";
$sqlpart2 .= $value . ".qty as " . $value . " ";
$sqlpart3 .= "SELECT item_no FROM " . $value . " ";
$sqlpart4 .= "LEFT JOIN " . $value . " ON T1.item_no=" . $value . ".item_no ";
}
$SQL = "SELECT T1.item_no,COALESCE(" . $sqlpart1 . ") as price," . $sqlpart2;
$SQL .= "FROM (" . $sqlpart3 . ")T1 " . $sqlpart4;
echo $SQL;
?>
Be careful to avoid the risk of SQL injection: compare the posted values against a closed list of existing store table names and reject any other value.
Note also that not only the FROM clause is influenced by the user's choices, but also the SELECT clause. So you have two dynamic parts in your SQL statement.
You could use this code which makes use of array_intersect, implode and array_map:
$selected_stores = $_POST['store'];
// Protect against SQL-injection by only accepting known tables:
$all_stores = array("M1", "M2", "M3");
$selected_stores = array_intersect($selected_stores, $all_stores);
// Build dynamic part of the FROM clause
$from = implode("
UNION
", array_map(function ($store) {
return "SELECT '$store' as store, item_no, price, qty FROM $store";
}, $selected_stores));
// Build dynamic part of the SELECT clause
$cols = implode(",
", array_map(function ($store) {
return "CASE store WHEN '$store' THEN qty END AS $store";
}, $selected_stores));
$sql = "
SELECT item_no,
MAX(price) as price,
$cols
FROM ( $from ) data
GROUP BY item_no
";
The SQL generated looks like this:
SELECT item_no,
MAX(price) as price,
CASE store WHEN 'M1' THEN qty END AS M1,
CASE store WHEN 'M2' THEN qty END AS M2
FROM ( SELECT 'M1' as store, item_no, price, qty FROM M1
UNION
SELECT 'M2' as store, item_no, price, qty FROM M2 ) data
GROUP BY item_no
See also this SQL fiddle.
As a side comment: I would advise to combine all store tables into one table, which would have an additional column indicating the store. This is more in line with normalised database design and will give more advantages than disadvantages in terms of searching, sorting, performance, and simplicity.

How to echo single name from multiple same name from the same table

I am having trouble on how to display/echo single name from multiple value of (three) of the same name. My table name is usages.
id | account | amount | date
1 Purchase 10000 2015-04-01
2 Repair 200000 2015-04-02
3 Purchae 30000 2015-04-03
4 Purchase 10000 2015-04-04
5 Mafanikio 20000 2015-04-04
6 Simon 20000 2015-04-04
7 Spare 10000 2015-04-04
This is my PHP code:
global $database;
$i = 1;
$seL = "SELECT * FROM usages ";
$Q =$database->query($seL);
$num = $database->query_to_num_rows($seL);
if($num !=0) {
while ($fet =$database->fetch_array($Q)) {
$show =$fet['account'];
$sel2 ="SELECT * FROM usages WHERE account = '$show' GROUP BY account ";
$Que =$database->query($sel2);
$numR =$database->query_to_num_rows($sel2);
if($numR ==1) {
$arr = $database->fetch_array($Que);
echo "<br> ".$i++. " ". $a =$arr['account'];
The result here is:
1 Purchase
2 Repair
3 Purchase
4 Purchase
5 Mafanikio
6 Simon
7 Spare
My desired answer I need like this
1 Purchase
2 Repair
3 Mafanikio
4 Simon
5 Spare
I don't want the same name to repeat.
use DISTINCT
In a table, a column may contain many duplicate values; and sometimes you only want to list the different (distinct) values.
The DISTINCT keyword can be used to return only distinct (different) values.
Try this as your query:
"SELECT DISTINCT * FROM usages WHERE account = '$show'";
"SELECT DISTINCT * FROM usages ";
use whichever your are trying to get single name from.
global $database;
$i = 1;
$seL = "SELECT DISTINCT account FROM usages ";
$Q =$database->query($seL);
$num = $database->query_to_num_rows($seL);
if($num !=0) {
while ($fet =$database->fetch_array($Q)) {
$show =$fet['account'];
$sel2 ="SELECT * FROM usages WHERE account = '$show'";
$Que =$database->query($sel2);
$numR =$database->query_to_num_rows($sel2);
if($numR ==1) {
$arr = $database->fetch_array($Que);
echo "<br> ".$i++. " ". $a =$arr['account'];

Craft calculator loop

I'm trying to do a craft calculator for a game.
I have a database who looks like this :
Crafts :
id item_id item_name amount
3 1895 a 5
8 2486 c 1
Craft_materials :
id craft_id item_id item_name amount
1 3 2486 c 15
2 3 5302 d 23
3 3 5698 e 2
4 8 2014 f 3
And here is my query to retrieve the data :
$craftProduct = $db->query("SELECT * FROM crafts WHERE item_id=$item");
$craftProduct->setFetchMode(PDO::FETCH_OBJ);
$product = $craftProduct->fetch();
$craftID = $product->id;
$craftMaterial = $db->query("SELECT * FROM craft_materials WHERE craft_id=$craftID");
$craftMaterial->setFetchMode(PDO::FETCH_OBJ);
echo '<ul>';
echo '<li>'.$product->item_name.' ('.$product->amount.')</li>';
echo '<ul>';
while($material = $craftMaterial->fetch()){
echo '<li>'.$material->item_name.' ('.$material->amount.')</li>';
}
echo '</ul>';
echo '</ul>';
What I want to do is take the material id and check if it corresponds with a craft. If it does, I want to show the material needed to craft it.
I want something which looks like this :
- a (5)
- c (15)
- f (3)
- d (23)
- e (2)
However, I don't know how to do the loop. Can anyone help me?
Here is a basic recursion setup:
function render_item($item_id) {
$product = fetch_item($item_id); // select from crafts where item_id = $item_id
$children = "";
foreach (fetch_children($product->id) as $child_id) { // select from craft_mats where craft_id = $product->id
$children .= render_item($child_id);
}
echo "<li>" . $product->item_name . "<ul>" . $children . "</ul></li>";
}
echo "<ul>" . render_item(1895) . "</ul>"

Displaying only 1 column in last row

I've checked the queries in phpMyAdmin a lot of times and is dead sure they are absolutely correct. Also, if I manually write the loop 3 times, setting 2,3,4 instead of incrementing counter it still displays ONLY one column in last row. First two rows result is accurate.
foreach($row as $rec) is basically running 17 times from another query which is printing table headers.
$by_type1 = array("First","Second+","Final");
$counter = 2; //this counter represents type of interview (2-First, 3-Second+, 4-Final)
foreach ($by_type1 as $type1)
{
$table_row = '<tr><td class="rborder">'.$type1.'</td>';
foreach ($row as $rec)
{
$id=$rec['id'];
$qry2 = "SELECT
CONCAT( r.fname, ' ', r.lname ) AS rname,
ch.status_id as Type,
count(ch.status_id) as number
FROM candidateJoborderHistory ch
LEFT JOIN candidates_info c ON ch.candidate_id = c.candidate_id
LEFT JOIN recruiters r ON c.recruiter_id=r.recruiter_id
LEFT JOIN interviewtypes i ON ch.interview_id = i.interview_id
WHERE c.recruiter_id = $id AND UNIX_TIMESTAMP(ch.date_interview) BETWEEN 1401667200 AND
1402099200 AND ch.status_id = $counter
group by ch.status_id";
global $conn;
$conn->open();
$stmt2 = $conn->prepare($qry2);
$stmt2->execute();
$row2 = $stmt2->fetchAll();
foreach($row2 as $row_x)
{
$table_row .= '<td>'.$row_x['number'].'</td>';
}
}
$table_row .='</tr>';
echo $table_row . "\n";
$counter++;
}
What I want is:
First 6 6 4 4 11 6 12 3
Second+ 3 1 2 1 3
Final 3 2 1 4 1
But what I am getting is:
First 6 6 4 4 11 6 12 3
Second+ 3 1 2 1 3
Final 3
Well I think that is a MySQL approach, I've made some changes to your code and explained them:
$by_type1 = array(2=>"First", 3=>"Second+", 4=>"Final");
$counter = 2; //this counter represents type of interview (2-First, 3-Second+, 4-Final)
// Open connection first.
global $conn;
$conn->open();
// Using key => value array gets code simple
foreach ($by_type1 as $counter=>$type1)
{
$table_row = '<tr><td class="rborder">'.$type1.'</td>';
foreach ($row as $rec)
{
$id=$rec['id'];
$qry2 = "SELECT
CONCAT( r.fname, ' ', r.lname ) AS rname,
ch.status_id as Type,
count(ch.status_id) as number
FROM candidateJoborderHistory ch
LEFT JOIN candidates_info c ON ch.candidate_id = c.candidate_id
LEFT JOIN recruiters r ON c.recruiter_id=r.recruiter_id
LEFT JOIN interviewtypes i ON ch.interview_id = i.interview_id
WHERE c.recruiter_id = $id
AND UNIX_TIMESTAMP(ch.date_interview)
BETWEEN 1401667200 AND 1402099200
AND ch.status_id = $counter
GROUP BY ch.status_id";
$res = $conn->prepare($qry2);
$res->execute();
// Loop to get data...
while($row_x = $res->fetch(PDO::FETCH_ASSOC))
{
$table_row .= '<td>'.$row_x['number'].'</td>';
}
}
$table_row .='</tr>';
echo $table_row . "\n";
}
$conn->close();
Code isn't tested but if you have any doubt just ask. Hope it helps!
References:
Prepared Statements
Executing Statements

PHP & MYSQL - 100,000+ Rows 140+ Columns - If loop with 50+ queries - Find a better way

I have a database with ~100000 rows and ~150 columns of data in the format:
data_id data_name monthly_data1 monthly_data2 monthly_data3 "" ""
0 product1_data-item1 20 30 10 "" ""
1 product1 data-item2 10 30 20 "" ""
2 product1 data-item3 9 10 23 "" ""
3 product2 data-item1 40 13 12 "" ""
4 product2 data-item2 31 12 32 "" ""
5 product2 data-item3 23 49 23 "" ""
The data set above is a basic sample of the data, in reality, there are over 2000 products with 50+ data-items each (approx 100,000 rows) and approx 140 columns of data which is annual data.
I need to search the database for each data item - for each product (i.e. each row) and determine if the value in the columns month_data1 through month_data140 for each data-item is within a pre-determined minimum/ maximum range for that specific data item.
Here is the format of my code as is, it is working, just very slowly, approx 20 seconds to complete all 50 checks for each product for each year.
$numberProducts = 2000;
$numberLineItems = 50;
$numberMonths = 140;
for($m=0;$m<$numberMonths;$m++){
for($p=0;$p$numberProducts;$p++){
$dataMonth = 'data_month'.$m+1;
$q="SELECT $dataMonth FROM product_table WHERE data_id='".($p*$numberLineItems)."'";
$q=mysql_query($q);
while($row=mysql_fetch_assoc($q)){
$dataVal = $row[$dataMonth];
}
mysql_free_result($q);
if(($dataVal>=$dataMin1)&&($dataVal<=$dataMax1)){
$q="SELECT $dataMonth FROM product_table WHERE data_id='".($p*$numberLineItems+1)."'";
$q=mysql_query($q);
while($row=mysql_fetch_assoc($q)){
$dataVal = $row[$dataMonth];
}
mysql_free_result($q);
if(($dataVal>=$dataMin2)&&($dataVal<=$dataMax2)){
$q="SELECT $dataMonth FROM product_table WHERE data_id='".($p*$numberLineItems+2)."'";
$q=mysql_query($q);
while($row=mysql_fetch_assoc($q)){
$dataVal = $row[$dataMonth];
}
mysql_free_result($q);
if(($dataVal>=$dataMin3)&&($dataVal<=$dataMax3)){
.
.
.
AND SO ON. All the way to the 50th data item for each product for each month, checking to see if the monthly value for that data item for that product is within a predetermined range --- The pre-determined range ( dataMin/dataMax) is different for each separate data item per product but the same per specific data item between products.
I am looking for a way to speed up the code, different query combination, server setting, loop style, etc. which may help to omptimize things and get down to just a few seconds required for output. ANY ideas would be appreciated.
My first thought was to change the select statement to select the entire database
$q = "SELECT * FROM product_table";
and put the data into a multidimensional array to do the min/max checks and avoid the 14,000,000 queries but I get hit the "out of memory" limit.
There has got to be a better way...
You Can Try , As Like following ... I am sending without test, If you find out any syntax error, Please try with correction those syntax error ...
$numberMonths = 140;
$minRange = 20;
$maxRange = 35;
$dataItemArray = array();
$q=mysql_query("SELECT * FROM product_table");
while($row=mysql_fetch_assoc($q)){
for($i = 1 ; $i <= numberMonths; $i++){
$nowColumn = monthly_data$i;
if(($row[$nowColumn] >= $minRange) AND ($row[$nowColumn] <= $maxRange))
$dataItemArray = $row['data_name']
}
}
Here is one approach :
for ($i = 1; $i <= 50; $i++) {
$$min = 'dataMin' . $i;
$$max = 'dataMax' . $i;
$dataMonth = 'data_month' . $i;
//Query to get all the data_id's that fall in the given range
//If you know that many of your data_month values will be in the min max range,
//you can use the opposite of this query.
//i.e select those data_ids that are not in the range -- this result set
//will be significantly smaller and will consume much less time and memory
/*
* eg:
* $res = mysql_query('SELECT data_id
FROM product_table
WHERE ' . $dataMonth . ' > ' . $$max . ' OR ' . $dataMonth . ' < ' . $$min);
*/
$res = mysql_query('SELECT data_id
FROM product_table
WHERE ' . $dataMonth . ' <= ' . $$max . ' AND ' . $dataMonth . ' >= ' . $$min);
if (mysql_num_rows($res) > 0) {
while ($row = mysql_fetch_assoc($res)) {
//this arr will contain the data_ids who are in the given range for the moth $dataMonth
$finalArr[$dataMonth][] = $row['data_id'];
}
}
mysql_free_result($res);
//$finalArr will have months as keys and data_ids who have the value in the specified range
}

Categories