Retrieve multiple rows from multiple tables - php

I am trying retrieve multiple rows from multiple tables but I think I am not doing it in the right way. The project is kind of a shop online, I have 3 tables in it: orders, orderdetails and services, which all are linked with an ID:
I have Order ID and Service ID in orderdetails' table, it means I inserts a row for each item on the basket linked to Service ID to see which service is, and Order Id to check for which order are. Example:
services table
-
service_id|name |price
------------------------
2 |Tech |100
------------------------
4 |Support|150
------------------------
10 |Mainten|50
------------------------
orders table
-
order_id|customer_id|name|lastname
----------------------------------
10 |16 |John|Smith
----------------------------------
orderdetails table
-
orderdetails_id|order_id|service_id|price|quantity
--------------------------------------------------
1 |10 |2 |100 |4
--------------------------------------------------
2 |10 |4 |150 |2
--------------------------------------------------
3 |10 |10 |50 |1
--------------------------------------------------
I inserts service's price on orderdetails table because maybe the services price can change AFTER a customer order it.
At this moment I have this query:
$query = $this->db->prepare(
'SELECT orders.*, orderdetails.*, services.*
FROM orders
LEFT JOIN orderdetails
ON orderdetails.order_id = orders.order_id
LEFT JOIN services
ON orderdetails.service_id = services.service_id
WHERE orders.order_id = ?
AND orders.customer_id = ?');
And I got this result:
stdClass Object
(
[order_id] => 10
[customer_id] => 16
[name] => Tech
[lastname] => Smith
[orderdetails_id] => 1
[service_id] => 2
[price] => 100
[quantity] => 4
)
stdClass Object
(
[order_id] => 10
[customer_id] => 16
[name] => Support
[lastname] => Smith
[orderdetails_id] => 2
[service_id] => 4
[price] => 150
[quantity] => 2
)
stdClass Object
(
[order_id] => 10
[customer_id] => 16
[name] => Mainten
[lastname] => Smith
[orderdetails_id] => 3
[service_id] => 10
[price] => 50
[quantity] => 1
)
I have two problems. The 1st problem is I have the same column name in orders table and services table. The 2nd is the query returns all the information (because I know I am not querying well), but I expect to receive something like this:
stdClass Object
(
[order_id] => 10
[customer_id] => 16
[name] => John
[lastname] => Smith
[orderdetails_id] => 1
[service_id] => 10
[price] => 50
[quantity] => 1
[service_name] => Mainten
[orderdetails_id2] => 2
[service_id2] => 4
[price2] => 150
[quantity2] => 2
[service_name2] => Support
[orderdetails_id3] => 3
[service_id3] => 2
[price3] => 100
[quantity3] => 4
[service_name3] => Tech
)
I mean, I am not an expert in SQL Queries, and I read a lot, but I think you guys could help me to figure it out this because I have other two tables to link with: customer-service-worker who will get the order to process, and area's table who will receive the order.
I use this code for getting the objects:
$array = array();
while($loop = $result->fetch_object()){ $array[] = $loop; }
return $array;

The problem is that you are using fetch_object() for getting the result but you are not renaming in the query the columns with the same name, and since you can't have two different object attributes with the same name the rest of columns are discarded.
You can either use other method for getting the values like fetch_row() or change the query to rename columns with the same name, for example:
SELECT orders.*, orderdetails_id, service_id, orderdetails.price as detail_price, quantity,
services.name as service_name, services.price as service_price
FROM orders
LEFT JOIN orderdetails ON orderdetails.order_id = orders.order_id
LEFT JOIN services ON orderdetails.service_id = services.service_id
WHERE orders.order_id = ?
As a side note if order_id is the primary key of orders you don't need to use customer_id in the where condition, and if the primary key is composed of both columns (i.e., you can have the same order ID for different customers) I recommend change it and use only order_id.

Related

Consolidate Query Results into Single Index

I have a table (usermeta - 2nd below) that is causing my trouble. I need to get 2 values from this table in 1 query and return the results in a single array index.
One table (users) contains the following:
ID | user_email | display_name |
-----+------------------+---------------|
8 | bob#bobjones.com | bob jones |
9 | rob#robsmith.com | rob smith |
Another table (usermeta) contains:
user_id | meta_key | meta_value |
----------+--------------+--------------|
8 | phone_number | 4441234433 |
8 | rep_id | abc123 |
9 | phone_number | 5552323322 |
9 | rep_id | xyz456 |
My SQL query:
function get_this() {
global $db;
$get = $db->get_results("
SELECT
um.meta_key, um.meta_value, um.user_id, user.user_email, user.display_name
FROM
users as user
LEFT OUTER JOIN
usermeta as um
ON
user.ID = um.user_id
WHERE
um.meta_key='rep_id'
OR
um.meta_key='phone_number'
");
return $get;
}
Calling the function: $foo = get_this(); returns this array:
Array
(
[0] => stdClass Object
(
[meta_key] => phone_number
[meta_value] => 4441234433
[user_id] => 8
[user_email] => bob#bobjones.com
[display_name] => bob jones
)
[1] => stdClass Object
(
[meta_key] => rep_id
[meta_value] => abc123
[user_id] => 8
[user_email] => bob#bobjones.com
[display_name] => bob jones
)
[2] => stdClass Object
(
[meta_key] => phone_number
[meta_value] => 5552323322
[user_id] => 9
[user_email] => rob#robsmith.com
[display_name] => rob smith
)
[3] => stdClass Object
(
[meta_key] => rep_id
[meta_value] => xyz456
[user_id] => 9
[user_email] => rob#robsmith.com
[display_name] => rob smith
)
)
The problem I am having is in consolidating the data based on user_id. I'd like to return an array that includes both their phone number AND their rep id along with their user_id, user_email and display_name in one index so I can print something like: bob jones, bob#bobjones.com, 4441234433, abc123
I clearly do not know what question to ask as I've spent 3 days now researching this site and the Internet and trying various combinations of CASE, AND, OR, WHERE, UNION, GROUP etc. to no avail. I am happy to manage this on the PHP end but have failed in that department as well.
You could put two different meta values on the same row of result by joining twice to the meta table:
SELECT user.user_id, user.user_email, user.display_name,
um1.meta_value AS `rep_id`,
um2.meta_value AS `phone_number`
FROM users AS user
LEFT OUTER JOIN usermeta AS um1
ON user.ID = um1.user_id AND um1.meta_key = 'rep_id'
LEFT OUTER JOIN usermeta AS um2
ON user.ID = um2.user_id AND um2.meta_key = 'phone_number'

How to create a three dimensional array from sql query PHP

I have 2 tables in a database, 1 of them are linked with a foreign key to the first one. Each row on table 1 is linked to multiple rows in table 2. I am trying to make a query that looks at a WHERE from table 2 and returns multiple rows from table 2 which are sorted into the rows they linked with in table 1 and then put this all into one big multi dimensional array, so it should work something like this:
$array[0][column_name][0] this would use row 1 from table 1 and give me a the first result in the column called column_name
$array[1][column_name][0] this would use row 2 from table 1 and give me a the first result in the column called column_name
$array[1][column_name][3] this would use row 2 from table 1 and give me a the 4th result in the column called column_name
etc
How can I query this and store it in a 3 dimensional array using PHP.
I have tried to word this in as clear manner as possible, if you are unsure what I am asking, please comment and I will update my question to make it clearer.
Assume that we have two tables, Company and Employee:
Company
------------------
ID Company_Name
1 Walmart
2 Amazon.com
3 Apple
Employee
---------------------------------
ID Company_Id Employee_Name
1 1 Sam Walton
2 1 Rob Walton
3 1 Jim Walton
4 1 Alice Walton
5 2 Jeff Bezos
6 2 Brian T. Olsavsky
7 3 Steve Jobs
8 3 Tim Cook
The easiest way to envision a multi-dimensional (nested) array is to mimic the looping required to get it: outer loop is the company, inner loop is the employees:
// ignoring database access, this is just pseudo code
$outer = [];
// select id, company_name from company
foreach $companyResult as $companyRow {
// select * from employee where company_id = ? {$companyRow['id']}
$inner= [];
foreach $employee_result as $employeeRow {
$inner[] = $employeeRow; // ie, ['id'=>'1','Company_Id'=>'1','Employee_Name'=>'Sam Walton']
}
$outer[] = $inner;
}
print_r($outer);
// yields ====>
Array
(
[0] => Array
(
[0] => Array
(
[id] => 1
[Company_Id] => 1
[Employee_Name] => Sam Walton
)
[1] => Array
(
[id] => 2
[Company_Id] => 1
[Employee_Name] => Rob Walton
)
[2] => Array
(
[id] => 3
[Company_Id] => 1
[Employee_Name] => Jim Walton
)
[3] => Array
(
[id] => 4
[Company_Id] => 1
[Employee_Name] => Alice Walton
)
)
[1] => Array
(
[0] => Array
(
[id] => 5
[Company_Id] => 2
[Employee_Name] => Jeff Bezos
)
[1] => Array
(
[id] => 6
[Company_Id] => 2
[Employee_Name] => Brian T. Olsavsky
)
)
[2] => Array
(
[0] => Array
(
[id] => 7
[Company_Id] => 3
[Employee_Name] => Steve Jobs
)
[1] => Array
(
[id] => 8
[Company_Id] => 3
[Employee_Name] => Tim Cook
)
)
)
It is also possible to do if you use associative arrays. Consider the flat file that this query produces:
select company.id company_id, company.name company_name,
emp.id employee_id, emp.employee_name
from company
inner join employee on company.id = employee.company_id
-----
company_id company_name employee_id employee_name
1 Walmart 1 Sam Walton
1 Walmart 2 Rob Walton
1 Walmart 3 Jim Walton
1 Walmart 4 Alice Walton
2 Amazon.com 5 Jeff Bezos
2 Amazon.com 6 Brian T. Olsavsky
3 Apple 7 Steve Jobs
3 Apple 8 Tim Cook
Just use the primary IDs as the keys for your arrays:
$employeeList = [];
foreach($result as $row) {
$cid = $row['company_name'];
$eid = $row['employee_name'];
// avoid uninitialized variable
// $employeeList[$row['company_name']] = $employeeList[$row['company_name']] ?? [];
// easier to read version of above
$employeeList[$cid] = $employeeList[$cid] ?? [];
// assign it...
$employeeList[$cid][$eid] = $row;
}
Or, if you simply want each company row to hold an array of employee names,
$employeeList[$cid][] = $row['employee_name'];
The way that I've shown you is useful if you know the company_id and want to find the associated rows:
foreach($employeeList[2] as $amazon_guys) { ... }
But it's not at all useful if you're trying to group by employee, or some other field in the employee table. You'd have to organize the order of your indexes by your desired search order.
In the end, it's almost always better to simply do another query and let the database give you the specific results you want.

php list and select data using join tables

I have this database table structure for posts, categories and post_categories
posts:
| id | title | details
| 1 | test | test details
categories:
| id | name | parent_id
| 1 | cat one | 0
| 2 | cat two | 0
| 3 | cat three | 0
post_categories
| category_id | post_id
| 1 | 1
| 2 | 1
I insert multiple categories using input checkbox for each post in post_categories table. Now in update page(domain.com/admin/posts/1/edit) i need to show categories list and checked input checkbox.can i generate this output using join two table(categories and post_categories) Or I need to separate two query(first from post_category table and second from categories table) like this output?! (my choice is join method so how to generate use join method?)
<input type="checkbox" value="1" checked> cat one
<input type="checkbox" value="2" checked> cat two
<input type="checkbox" value="3"> cat three //unchecked (not in post_categories table)
update:
for list categories query:
$query = $this->db->table('categories')
->select('id, name, parent_id')
->get()
->getResultObject();
data in print_r:
Array
(
[0] => stdClass Object
(
[id] => 1
[name] => cat one
[parent_id] => 0
)
[1] => stdClass Object
(
[id] => 2
[name] => cat two
[parent_id] => 0
)
[2] => stdClass Object
(
[id] => 3
[name] => cat three
[parent_id] => 0
)
[3] => stdClass Object
(
[id] => 4
[name] => cat4
[parent_id] => 2
)
[4] => stdClass Object
(
[id] => 5
[name] => cat5
[parent_id] => 0
)
[5] => stdClass Object
(
[id] => 6
[name] => cat6
[parent_id] => 0
)
)
after join:
$query = $this->db->table('categories')
->select('id, name, parent_id')
->join('post_categories', 'post_categories.category_id = id','left outer')
->where('post_categories.post_id', $id)
->get()
->getResultObject();
and data is:
Array
(
[0] => stdClass Object
(
[id] => 1
[name] => cat one
[parent_id] => 0
)
[1] => stdClass Object
(
[id] => 4
[name] => cat two
[parent_id] => 2
)
)
result after join is false.
You can do this with one query but for this, you need one extra column in query for checking category is using or not.
SELECT id, name, if(pc.cat_id IS NULL,0,1) as `value` from categories as ct LEFT JOIN post_categories as pc on pc.cat_id = ct.id
demo link
I hope this query will fulfill your requirements.
If you need more detail about this you can visit this StackOverflow thread
MySQL Join and create new column value
You can run below query to get the categories selected:
select post_categories.category_id from posts
left join post_categories on post_categories.post_id=posts.id
left join categories on post_categories.category_id=categories.id;
get these ids to an array and within your category loop check if the ids are in array and make them checked.
quite easy !

Combining data from multiple arrays into a final array for output in CSV

I'm trying to restructure my arrays so that I can get the desired output in one final array that will be used to fill a csv, eventually spreadsheet.
My first msyql query and the resulting array give me this structure:
array(
[0] => Array
(
[sku_id] => 1
[dealer_id] => 1976
[locations] => 1
[groupID] => 1
[frame] => 1051
[cover] => 1150
[color] => 99
[start_date] => 2018-03-
)
[1] => Array
(
[sku_id] => 1
[dealer_id] => 5400
[locations] => 1
[groupID] => 1
[frame] => 1051
[cover] => 1150
[color] => 99
[start_date] => 2017-04-
)
[2] => Array
(
[sku_id] => 1
[dealer_id] => 11316
[locations] => 1
[groupID] => 1
[frame] => 1051
[cover] => 1150
[color] => 99
[start_date] => 2017-02-
)
)
So I get 3 records, each for the same product (sku_id) but a different customer(dealer_id). Then I match this data up in a similar table in DB2 to get quantity for each order of the sku_id. That 2nd query/array shows this:
Array
(
[0] => Array
(
[CSTNOC] => 1976
[TOTALQTY] => 2
)
[1] => Array
(
[CSTNOC] => 5400
[TOTALQTY] => 5
)
[2] => Array
(
[CSTNOC] => 11316
[TOTALQTY] => 14
)
)
However, this is what I'm trying to achieve.
In the below example, sku_id, groupID and location would come directly from the first query. Days is the number of days between start_date (from query 1) and curDate(). Qty is orqtyc from query 2 and Average is a calculation based on locations, days and qty. Each row is based on a different customer, so even though the below example is for one sku, the rows signify a different customer with different locations, qty, etc.
SKU_id | groupID | locations (n) | Days (x) | Qty(q) | Average (x/(q/n))
--------------------------------------------------------------------------------
123 1 3 120 15 24
123 1 2 12 6 4
The Report or CSV would then only show one line item for the above:
SKU | Group | Average Days | Total units sold
123 | 1 | 28 | 21
Basically, for every sku I'm getting those records and then I need to do those calculations by row, and get a total of those values for each sku. The report will only have one line item per sku.
I have everything I need to get there, but how can I structure a final array to get my desired outputs like above?
FULL SCRIPT:
$skuQuery = "
SELECT
sku_id,
dealer_id,
locations,
s.sku_group_id as groupID,
s.frame as frame,
s.cover1 as cover,
s.color1 as color,
start_date - interval 7 day as start_date
from products p
inner join skus s on p.sku_id = s.id
where curdate() between p.start_date and p.expire_date
group by sku_id, dealer_id
limit 3";
$skuRslt = mysqli_query($conn,$skuQuery);
while($skuRow = mysqli_fetch_assoc($skuRslt)){
$skuResult[] = $skuRow;
$dealerQuery = "
SELECT
cstnoc,
sum(orqtyc) as TotalQTY
from table
where cstnoc = {$skuRow['dealer_id']}
AND framec = {$skuRow['frame']}
AND colr1c = {$skuRow['color']}
AND covr1c = {$skuRow['cover']}
AND extd2d >= " . str_replace('-', '', $skuRow['start_date']) . "
group by cstnoc, framec
";
$dealerRslt = odbc_exec($DB2Conn, $dealerQuery);
foreach($skuResult as $skuRow){
while($dealerRow = odbc_fetch_array($dealerRslt)){
$dealerResult[] = $dealerRow;
}
}
print_r($dealerResult);
}

Streamlining array to get total for each key

I'm trying to build arrays based off of two queries, one in mysql one on db2. I've built the main arrays, the first dealing with product info and the 2nd dealing with customer info but they have some values in common.
This is working as far as structure but I've been trying to build a final array to contain data from the initial arrays that will build a report.
For now, I'm just going to try and output a report or csv file for each sku_id from the first query, and the total number/ quantity from the 2nd query.
The 2nd query/array contain multiple attributes on a per product/customer basis.
(
[1] => 2
)
Array
(
[1] => 5
)
Array
(
[1] => 14
)
Basically, I pulled three records from the first table for sku_id '1' and matched those three records in db2 for the same sku. Each element corresponds to a customer, so this structure says "customer A had 2 of sku_id 1, customer B had 5 of sku_id 1 and customer C had 14 of sku_id 1'
For the purpose of the report, each line will be for each sku so I want to instead output this as:
array
(
[1] => 21
)
Showing that overall, sku_id 1 had a total of 21. So I still want to keep my initial arrays structured like they are, but I want to modify my final array to just give me line items for the report.
How can I do that?
$skuQuery = "
SELECT
sku_id,
dealer_id,
locations,
s.sku_group_id as groupID,
s.frame as frame,
s.cover1 as cover,
s.color1 as color,
start_date - interval 7 day as start_date
from products p
inner join skus s on p.sku_id = s.id
where curdate() between p.start_date and p.expire_date
group by sku_id, dealer_id
limit 3";
$skuRslt = mysqli_query($conn,$skuQuery);
while($skuRow = mysqli_fetch_assoc($skuRslt)){
$skuResult['sku_id'] = $skuRow;
$dealerQuery = "
SELECT
cstnoc,
sum(orqtyc) as TotalQTY
from table
where cstnoc = {$skuRow['dealer_id']}
AND framec = {$skuRow['frame']}
AND colr1c = {$skuRow['color']}
AND covr1c = {$skuRow['cover']}
AND extd2d >= " . str_replace('-', '', $skuRow['start_date']) . "
group by cstnoc, framec
";
$dealerRslt = odbc_exec($DB2Conn, $dealerQuery);
$dealerResult = [];
foreach($skuResult as $skuRow){
while($dealerRow = odbc_fetch_array($dealerRslt)){
if ( !isset($dealerResult[$dealerRow['CSTNOC']]) ) {
$dealerResult[$dealerRow['CSTNOC']] = 0;
}
$dealerResult[$dealerRow['CSTNOC']] += $dealerRow['TOTALQTY'];
}
}
$skuTots = array_fill_keys(array_unique(array_column($skuResult, 'sku_id')), 0);
foreach ($skuResult as $rec) {
$skuTots[$rec['sku_id']] += $dealerResult[$rec['dealer_id']];
}
print_r($skuTots);
}
UPDATE:
This is the print out for my first two arrays:
Array
(
[0] => Array
(
[sku_id] => 1
[dealer_id] => 1976
[locations] => 1
[groupID] => 1
[frame] => 1051
[cover] => 1150
[color] => 99
[start_date] => 2018-03-
)
[1] => Array
(
[sku_id] => 1
[dealer_id] => 5400
[locations] => 1
[groupID] => 1
[frame] => 1051
[cover] => 1150
[color] => 99
[start_date] => 2017-04-
)
[2] => Array
(
[sku_id] => 1
[dealer_id] => 11316
[locations] => 1
[groupID] => 1
[frame] => 1051
[cover] => 1150
[color] => 99
[start_date] => 2017-02-
)
)
Array
(
[0] => Array
(
[CSTNOC] => 1976
[TOTALQTY] => 2
)
[1] => Array
(
[CSTNOC] => 5400
[TOTALQTY] => 5
)
[2] => Array
(
[CSTNOC] => 11316
[TOTALQTY] => 14
)
)
And here is an example of my end result I'm trying to achieve:
In the below example, sku_id, groupID and location would come directly from the first query. Days is the number of days between start_date (from query 1) and curDate(). Qty is orqtyc from query 2 and Average is a calculation based on locations, days and qty. Each row is based on a different customer, so even though the below example is for one sku, the rows signify a different customer with different locations, qty, etc.
SKU_id | groupID | locations (n) | Days (x) | Qty(q) | Average (x/(q/n))
--------------------------------------------------------------------------------
123 1 3 120 15 24
123 1 2 12 6 4
The Report or CSV would then only show one line item for the above:
SKU | Group | Average Days | Total units sold
123 | 1 | 28 | 21
Basically, for every sku I'm getting those records and then I need to do those calculations by row, and get a total of those values for each sku. The report will only have one line item per sku.

Categories