mysql select and join on multiple tables - php

I'm having tables like
product table: product_id | code
group table: id | fk-product_id | id_code
grade table id | fk-product_id | id_grade
person table id | fk-product_id | id_person
my sql query is:
"SELECT *
FROM product
JOIN group ON group.product_id = product_id
JOIN grade ON grade.product_id = product_id
JOIN person ON person.product_id = product_id
WHERE product.code='".$productCode."'");
I get the wright result, but there is too much of rows. I thing that I'm doing overkill.
All product are for sure in the table "product" but it's not necessary that the same "id_product" is in the table "group", "grade" or "person".
In my result are a lot of rows where my result is repeted. I there any way to avoid those duplication?
Is there better way to perform my query?

From your original query, you have listed the column in the group, grade and person table are
'fk-product_id' but your query is showing as just 'product_id'. So, I am implying your real column is just 'product_id' and the 'fk-' was just a reference that it was the foreign key to products table.
Now, that said, the equality comparison is just product_id. Since you are not qualifying it with alias.field, it is probably grabbing everything since each record in group will always have its own product_id = its own product_id.
In addition, you mention that not all tables will have a matching product ID, so you will need LEFT-JOINs for the other tables... Adjust to something like this
SELECT
p.*,
gp.id_code,
gd.id_grade,
per.id_person
FROM
product p
LEFT JOIN group gp
ON p.product_id = gp.product_id
LEFT JOIN grade gd
ON p.product_id = gd.product_id
LEFT JOIN person per
ON p.product_id = per.product_id
WHERE
p.code='".$productCode."'";
But I would head caution for sql-injection as you could get malicious values in your $productCode variable. Make sure you have it properly cleaned and escaped.
#5er, Left-Join says for each record on the left-side (first in this case is the Product Table), I want all records... and oh... by the way... I have the other tables (group, grade and persons). They MAY have a record too that has the same Product_ID value as the product table. If so, grab those pieces too, but don't exclude the original product record.
Now, why your query was failing, and I thought I described it well, but apparently not. You were getting a Cartesian result which means for every one record in the left-table (product), you were getting EVERY record in the RIGHT-side table... So, for a single product, and if you had 20 group, 10 grades and 100 people, you were basically getting 20,000 records.
So your JOIN
JOIN group ON group.product_id = product_id
WOULD have worked, but had less records IF you qualified with the PRODUCT reference
JOIN group ON group.product_id = PRODUCT.product_id
Otherwise, it was just comparing its own product_ID to itself and saying... Yup, these to product IDs match (even though it was the same record), it returned it. The engine can't guess for you which table you meant for the second part of the join, especially when there were a total of 4 tables referenced in the query, and EACH had a "Product_ID" column. So, I strongly suggest that for ALL your queries, qualify ALL fields as alias.field, including those of the select field list. Then, anyone else trying to help you in the future, or even take over where you left-off know where the fields are. Prevent ambiguity in your queries.

Your select does not match the table/column names above it. For example in table product you say you have column id_product, but in select you use product.id instead of product.id_product. That might be totally different column.

Your results are repeating because the JOIN is joining tables, but you are not filtering those cases where one JOIN matches, while the other isn't.
Try this:
SELECT *
FROM product
JOIN group ON group.product_id = product.id
JOIN grade ON grade.product_id = product.id
JOIN person ON person.product_id = product.id
WHERE product.code='".$productCode."'
GROUP BY product.id

Related

Create table report from joining table in MySQL

I have a proble with my query, i have to create report but there is no same data.
here is my database https://www.db-fiddle.com/f/2bA7StrBpz18tLFgAQh2QV/1
and this is my query example but the result is wrong :
SELECT
a.IdBukti,c.LineName,a.LineID,a.Tanggal,b.TypeProduksi AS partnamemonthly,a.PartID AS partmonthly,a.QtyPlanning AS qtymonthly,
d.partnamedaily,d.partiddaily,d.qtydaily
FROM
trans_ppicbdt_dt a
INNER JOIN ms_part b ON b.PartId = a.PartID
INNER JOIN ms_line c ON c.LineID = a.LineID
INNER JOIN(SELECT
c.LineName,
a.LineID,
a.Tanggal,
b.TypeProduksi AS partnamedaily,
a.PartID AS partiddaily,
a.QtyPlanning AS qtydaily
FROM
trans_ppich a
INNER JOIN ms_part b ON b.PartId = a.PartID
INNER JOIN ms_line c ON c.LineID = a.LineID
WHERE
a.Tanggal = '2018-04-11' AND a.DivisiId='DI070' AND a.IdLocation='1'
GROUP BY
a.LineID,
a.PartID) d on d.LineID=a.LineID AND d.Tanggal=a.Tanggal
WHERE
a.Tanggal = '2018-04-11' AND a.DivisiId='DI070' AND a.IdLocation='1'
GROUP BY
a.LineID,
a.PartID
So i have 2 data, first monthlyplan and second daily plan.
And i want the result like this
Can you help me to create the report in one single query
It's easier (for me) to figure out how to formulate a query if I can see the relations in a glance. Here's how Yours would look:
(monthly) (daily)
trans_ppicbdt_dt ms_line trans_ppich
---------------- ----------- --------------
IdUnik (primary) ----> LineID <---- IdBukti (FK)*
LineID ------------/ \--- LineID
There are some problems with your structure that need to be fixed at the earliest opportunity:
ms_line needs a primary key. lineID should work if it is unique. (no such luck, it is not unique)
trans_ppich needs a primary key. IdBukti is a foreign key to yet another table.
There needs to be an index on lineID in all tables.
Tables need to be normalized. You shouldn't have to repeat data over several lines.
Nevertheless, we can still get to where you want to go. Now that I can see the relation, the query is really rather simple in theory, but I don't have all the required tables and fields:
SELECT DISTINCT line.LineName,
monthly.{unknown field} as monthlyPartName,
monthly.PartID as monthlyPartID,
monthly.{unknown field} as monthlyProcess,
monthly.{unknown field} as monthlyQty,
daily.{unknown field} as dailyPartName,
daily.PartID as dailyPartID,
daily.{unknown field} as dailyQty,
{unknown table}.{unknown field} as remarks
FROM ms_line as line
LEFT JOIN trans_ppicbdt_dt AS monthly ON line.lineID=monthly.lineID
LEFT JOIN trans_ppich AS daily ON line.lineID=daily.lineID
WHERE
{your where clause to filter results}
The way this works is it iterates through the (distinct) line items, and looks for a match in the monthly table (left join monthly). If there is a match, it adds the values. If there isn't a match, it simply adds null for values. Then it does the same for the daily table: if there is a match, it adds the values, otherwise it simply gives null for all daily values.
This will fall apart, however, if lineID is duplicated in rows of the monthly and daily tables.
I don't know where the remarks come from, so that is left as an exercise for you. :)

How to sum values across a set of categories, in SQL?

I want to get a list of category items and display the total amount linked to those categories. I have the "amount" field in the "transaction" table and I want to link it to category table.
This is how my tables are:
Category Master
Subcategory Master
Item Master
Transaction
So to get my "Amount" field to category, I would have to pass a certain common column between them. I have CategoryID in SubCategory master, subcatid in item master and similarly itemid in transaction.
Earlier when I grouped the amount using transaction date, the process went smoothly:
SELECT TransactionDate, SUM(Amount) FROM transaction GROUP BY MONTH(TransactionDate)
Now the problem I'm facing with grouping it using categoryname is that all of the amount seems to be =50 whereas it is still different in the database. I know that this is something really silly, but I am comparatively new to programming and not sure how to use logic appropriately.
SELECT categorymaster.CategoryName, transaction.Amount
FROM categorymaster
INNER JOIN subcategorymaster
INNER JOIN itemmaster
INNER JOIN transaction
GROUP BY categorymaster.CategoryName
This answer assumes your primary key in each table is named "ID". You didn't provide that info.
SELECT categorymaster.CategoryName, sum(transaction.Amount)
FROM categorymaster
INNER JOIN subcategorymaster
ON subcagetorymaster.CategoryId = categorymaster.ID
INNER JOIN itemmaster
ON itemmaster.SubCatId = subcategorymaster.ID
INNER JOIN transaction
ON transaction.ItemId = itemmaster.ID
GROUP BY categorymaster.CategoryName

Using multiple inner joins

I have four tables:
users, orders, orders_product and products.
They are connected to each other by foreign key
user tables contains: id, name, email and username.
product table contains: id, product_name, product_description and product_price
orders table contains: id, u_id(foreign key).
orders_product table contains: id, product_id(foreign key), order_id(foreign key).
Now I was trying to fetch the name of a user with the total price of a particular order that he has placed.
The maximum I could went for was something like this:
SELECT prod.order_id,
SUM(product_price) AS Total
FROM products
INNER JOIN
(SELECT orders.id AS order_id,
orders_product.product_id
FROM orders
INNER JOIN orders_product ON orders.id = orders_product.order_id
WHERE order_id=1) AS prod ON products.id = prod.product_id;
It showed me total price of a particular order. Now I have two questions:
Is that query correct. It looks like a very long query. Can the same result be achieved with a smaller one?
How to fetch the name of a user with the total price of a particular order that he has placed.
Hi some addition to #Gordon Linoff
your query seems ok.
if you store your price data in order_products it will be good and some benefit, one of these benefit is aggregation will be simple. Second benefit if product price change it will not affect to order.
Your query is correct for one order, but it can be improved:
Don't use a subquery unless necessary. In MySQL this introduces additional overhead.
You are only looking at one order, which seems on the light site. You should remove the where clause.
You should be using a group by because you want aggregation.
You need to join in the user table to get the name.
I also added table aliases (abbreviations for table names). This makes the query a bit more readable:
SELECT u.name, SUM(p.product_price) as Total
FROM orders_product op INNER JOIN
orders o
ON o.id = op.order_id INNER JOIN
products p
ON p.id = op.product_id INNER JOIN
users u
on o.userid = u.id
WHERE op.order_id = 1
GROUP BY u.name;
Your SQL is wrong. Because You want to calculate specific to user. But your SQL is specific to Order. Your SQL will give result for One Order. Please make it User Specific by giving user name or what ever is unique.

Retrieve values from multiple tables relationed

So, I have a table named clients, another one known as orders and other two, orders_type_a and orders_type_b.
What I'm trying to do is create a query that returns the list of all clients, and for each client it must return the number of orders based on this client's id and the amount of money this customer already spent.
And... I have no idea how to do that. I know the logic behind this, but can't find out how to translate it into a MySQL query.
I have a basic-to-thinkimgoodbutimnot knowledge of MySQL, but to this situation I've got really confused.
Here is a image to illustrate better the process I'm trying to do:
Useful extra information:
Each orders row have only one type (which is A or B)
Each orders row can have multiple orders_type_X (where X is A or B)
orders relate with client through the column client_id
orders_type_X relate with orders through the column order_id
This process is being made today by doing a query to retrieve clients, and then from each entry returned the code do another query (with php) to retrieve the orders and yet another one to retrieve the values. So basically for each row returned from the first query there is two others inside it. Needless to say that this is a horrible approach, the performance sucks and I thats the reason why I want to change it.
UPDATE width tables columns:
clients:
id | name | phone
orders:
id | client_id | date
orders_type_a:
id | order_id | number_of_items | price_of_single_item
orders_type_b:
id | order_id | number_of_shoes_11 | number_of_shoes_12 | number_of_shoes_13 | price_of_single_shoe
For any extra info needed, just ask.
If I understand you correctly, you are looking for something like this?
select c.*, SUM(oa.value) + SUM(ob.value) as total
from clients c
inner join orders o on c.order_id = o.id
inner join orders_type_a oa on oa.id = o.order_type_id AND o.type = 'A'
inner join orders_type_b ob on ob.id = o.order_type_id AND o.type = 'B'
group by c.id
I do not know your actual field names, but this returns the information on each customer plus a single field 'total' that contains the sum of the values of all the orders of both type A and type B. You might have to tweak the various names to get it to work, but does this get you in the right direction?
Erik's answer is on the right track. However, since there could be multiple orders_type_a and orders_type_b records for each order, it is a little more complex:
SELECT c.id, c.name, c.phone, SUM(x.total) as total
FROM clients c
INNER JOIN orders o
ON o.client_id = c.id
INNER JOIN (
SELECT order_id, SUM(number_of_items * price_of_single_item) as total
FROM orders_type_a
UNION ALL
SELECT order_id, SUM((number_of_shoes_11 + number_of_shoes_12 + number_of_shoes_13) * price_of_single_shoe) as total
FROM orders_type_b
) x
ON x.order_id = o.id
GROUP BY c.id
;
I'm making a few assumptions about how to calculate the total based on the columns in the orders_type_x tables.

Multiple items in a single MySQL row?

Using MySQL + PHP, I want to store several food_items for a single restaurant order in a MySQL database.
I have two tables: orders & food_items:
Many food_item_ids will be stored for a single order.
I'm just wondering, what's the best approach to storing the food_item_ids in the orders_table?
Should I store each food_item_id in a separate row of the orders table, or place all of a particular order's food_item_ids into a single row?
If you want 3NF (and you should unless and until you find performance is a problem), you should either:
store the order ID in each row of the food_item table (if food_items is a per-order table);
or use a many-to-many relationship (if food_items is just the possible things you can order).
With the first option, something like this would suffice:
Orders:
order_id
other stuff
FoodItems:
order_id
item_id
other stuff.
For the second option, a separate table provides the many-to-many relationship:
Orders:
order_id
other stuff
FoodItems:
item_id
other stuff.
FoodItemsInOrder:
order_id
item_id
count
In my opinion, all database tables should be designed in third-normal form. Once you table gets so big that performance may become an issue (and it will have to be very big), then you can start thinking about de-normalizing for speed.
For a restaurant, I cannot imagine the order and food item tables getting anywhere near big enough to warrant de-normalizing.
If each food item is unique to a particular order, then you should store the order ID in the food_items table, then you could query like this:
SELECT orders.id, food.id
FROM orders
INNER JOIN food ON orders.id = food.order_id
However if you have standard food items, e.g. "burger", "chips", "hot dog" then you should use three tables. One for orders, one for food items, and one to join the two:
SELECT orders.id, food.id
FROM orders
INNER JOIN orders_food ON orders.id = orders_food.order_id
INNER JOIN food ON orders_food.food_id = food.id

Categories