I'm not sure how to ask my question, so I'll explain what I'm trying to do: I'm building an app in Delphi XE, which should calculate the consumption of raw materials, based on products recipes and orders.
I have 5 tables: Orders, OrdersContent, Products, Raw Materials and Recipes. Each order is composed of a few products, and each product has it's own recipe of raw materials.
I already summed up all products from all orders using sql in Query1.
This is the command for Query1:
select Products.Price,
OrdersContent.ID_Product, sum(OrdersContent.QNT) as QNT_Sum,
(Products.Price * sum(OrdersContent.QNT)) as Value
from Orders, OrdersContent, Products
where Orders.ID = OrdersContent.ID_Order
and Products.ID = OrdersContent.ID_Product
group by
OrdersContent.ID_Product, Products.Price
This returns:
|Price | ID_Product | QNT_Sum | Value |
----------------------------------------
| 2 | 122521 | 150 | 300 |
| 10 | 366547 | 10 | 100 |
| xxx | xxxxxx | xxx | xxxxx|
It's exactly what I want.
So now I'm wondering if there's a way to calculate the raw materials consumption also using sql, as the only other way I know how to do this is to iterate through the whole Query1 and calculate raw materials consumption for each record(product) individually, add it to a new table and then sum up the results, which is very time consuming.
I'm pretty sure there must be a more efficient way to do this, but have no clue as to how or where to search how to do it. I'm not asking for the code, but some pointers or links to tutorials or examples.
I hope I'm clear enough, if not please do ask for more info.
Add Raw Materials and Recipe to your FROM clause with the appropriate joins. Group by raw materials. Remove id_product and price from your group by statement. Change the aggregation in your select to sum(products.price*orderscontent.qnt).
I'm guessing at your column names in Recipes and Raw Materials but here's the general idea.
select Recipes.ID_RAW_MATERIALS,
sum(OrdersContent.QNT) as QNT_Sum,
sum(Products.Price * OrdersContent.QN)) as Value
from Orders, OrdersContent, Products, Recipes, RawMaterials
where Orders.ID = OrdersContent.ID_Order
and Products.ID = OrdersContent.ID_Product
and Recipes.ID_PRODUCT = Products.ID
AND Recipes.ID_RAW_MATERIAL = Rawmaterials.ID
group by Recipes.ID_RAW_MATERIALS
Related
I take a Database course in which we have listings of AirBnBs and need to be able to do some SQL queries in the Relationship-Model we made from the data, but I struggle with one in particular :
I have two tables that we are interested in, Billing and Amenities. The first one have the id and price of listings, the second have id and wifi (let's say, to simplify, that it equals 1 if there is Wifi, 0 otherwise). Both have other attributes that we don't really care about here.
So the query is, "What is the difference in the average price of listings with and without Wifi ?"
My idea was to build to JOIN-tables, one with listings that have wifi, the other without, and compare them easily :
SELECT avg(B.price - A.price) as averagePrice
FROM (
SELECT Billing.price, Billing.id
FROM Billing
INNER JOIN Amenities
ON Billing.id = Amenities.id
WHERE Amenities.wifi = 0
) A, (
SELECT Billing.price, Billing.id
FROM Billing
INNER JOIN Amenities
ON Billing.id = Amenities.id
WHERE Amenities.wifi = 1) B
WHERE A.id = B.id;
Obviously this doesn't work... I am pretty sure that there is a far easier solution to it tho, what do I miss ?
(And by the way, is there a way to compute the absolute between the difference of price ?)
I hope that I was clear enough, thank you for your time !
Edit : As mentionned in the comments, forgot to say that, but both tables have idas their primary key, so that there is one row per listing.
Just use conditional aggregation:
SELECT AVG(CASE WHEN a.wifi = 0 THEN b.price END) as avg_no_wifi,
AVG(CASE WHEN a.wifi = 1 THEN b.price END) as avg_wifi
FROM Billing b JOIN
Amenities a
ON b.id = a.id
WHERE a.wifi IN (0, 1);
You can use a - if you want the difference instead of the specific values.
Let's assume we're working with data like the following (problems with your data model are noted below):
Billing
+------------+---------+
| listing_id | price |
+------------+---------+
| 1 | 1500.00 |
| 2 | 1700.00 |
| 3 | 1800.00 |
| 4 | 1900.00 |
+------------+---------+
Amenities
+------------+------+
| listing_id | wifi |
+------------+------+
| 1 | 1 |
| 2 | 1 |
| 3 | 0 |
+------------+------+
Notice that I changed "id" to "listing_id" to make it clear what it was (using "id" as an attribute name is problematic anyways). Also, note that one listing doesn't have an entry in the Amenities table. Depending on your data, that may or may not be a concern (again, refer to the bottom for a discussion of your data model).
Based on this data, your averages should be as follows:
Listings with wifi average $1600 (Listings 1 and 2)
Listings without wifi (just 3) average 1800).
So the difference would be $200.
To achieve this result in SQL, it may be helpful to first get the average cost per amenity (whether wifi is offered). This would be obtained with the following query:
SELECT
Amenities.wifi AS has_wifi,
AVG(Billing.price) AS avg_cost
FROM Billing
INNER JOIN Amenities ON
Amenities.listing_id = Billing.listing_id
GROUP BY Amenities.wifi
which gives you the following results:
+----------+-----------------------+
| has_wifi | avg_cost |
+----------+-----------------------+
| 0 | 1800.0000000000000000 |
| 1 | 1600.0000000000000000 |
+----------+-----------------------+
So far so good. So now we need to calculate the difference between these 2 rows. There are a number of different ways to do this, but one is to use a CASE expression to make one of the values negative, and then simply take the SUM of the result (note that I'm using a CTE, but you can also use a sub-query):
WITH
avg_by_wifi(has_wifi, avg_cost) AS
(
SELECT Amenities.wifi, AVG(Billing.price)
FROM Billing
INNER JOIN Amenities ON
Amenities.listing_id = Billing.listing_id
GROUP BY Amenities.wifi
)
SELECT
ABS(SUM
(
CASE
WHEN has_wifi = 1 THEN avg_cost
ELSE -1 * avg_cost
END
))
FROM avg_by_wifi
which gives us the expected value of 200.
Now regarding your data model:
If both your Billing and Amenities table only have 1 row for each listing, it makes sense to combine them into 1 table. For example: Listings(listing_id, price, wifi)
However, this is still problematic, because you probably have a bunch of other amenities you want to model (pool, sauna, etc.) So you might want to model a many-to-many relationship between listings and amenities using an intermediate table:
Listings(listing_id, price)
Amenities(amenity_id, amenity_name)
ListingsAmenities(listing_id, amenity_id)
This way, you could list multiple amenities for a given listing without having to add additional columns. It also becomes easy to store additional information about an amenity: What's the wifi password? How deep is the pool? etc.
Of course, using this model makes your original query (difference in average cost of listings by wifi) a bit tricker, but definitely still doable.
I've attempted to write a query but I've not managed to get it working correctly.
I'm attempting to retrieve where a specific product has been bought but where it also has been bought with other products. In the case below, I want to find where product A01 has been bought but also when it was bought with other products.
Data (extracted from tables for illustration):
Order | Product
123456 | A01
123457 | A01
123457 | B02
123458 | C03
123459 | A01
123459 | C03
Query which will return all orders with product A01 without showing other products:
SELECT
O.NUMBER
O.DATE
P.NUMBER
FROM
ORDERS O
JOIN PRODUCTS P on P.ID = O.ID
WHERE
P.NUMBER = 'A01'
I've tried to create a sub query which brings back just orders of product A01 but I don't know how to place it in the query for it to return all orders containing product A01 as well as any other product ordered with it.
Any help on this would be very grateful.
Thanks in advance.
You can use conditional SUM to detect if one ORDER group have one ore more 'A01'
CREATE TABLE orders
("Order" int, "Product" varchar(3))
;
INSERT INTO orders
("Order", "Product")
VALUES
(123456, 'A01'),
(123457, 'A01'),
(123457, 'B02'),
(123458, 'C03'),
(123459, 'A01'),
(123459, 'C03')
;
SELECT "Order"
FROM orders
GROUP BY "Order"
HAVING SUM(CASE WHEN "Product" = 'A01' THEN 1 ELSE 0 END) > 0
I appreciated Juan's including the DDL to create the database on my system. By the time I saw it, I'd already done all the same work, except that I got around the reserved word problem by naming that field Order1.
Sadly, I didn't consider that either of the offered queries worked on my system. I used MySQL.
The first one returned the A01 lines of the two orders on which other products were ordered too. I took Alex's purpose to include seeing all items of all orders that included A01. (Perhaps he wants to tell future customers what other products other customers have ordered with A01, and generate sales that way.)
The second one returned the three A01 lines.
Maybe Alex wants:
select *
from orders
where Order1 in (select Order1
from orders
where Product = 'A01')
It outputs all lines of all orders that include A01. The subquery makes a list of all orders with A01. The first query returns all lines of those orders.
In a big database, you might not want to run two queries, but this is the only way I see to get the result I understood Alex wanted. If that is what he wanted, he would have to run a second query once armed with output from the queries offered, so there's no real gain.
Good discussion. Thanks to all!
Use GROUP BY clause along with HAVING like
select "order", Product
from data
group by "order"
having count(distinct product) > 1;
I have a table with stock codes and quantity sold, but I would like to treat 2 different stock codes as one, the reason being is that one is imported and the other one locally produced but are the same product,
lets say
Product A - Imported, Stock code is abc123
Product A - Local, Stock code is aimp563
I want to sum over the quantity sold but treat the same product with and an imported stock code and local stock code as one. Is this possible?
Okay this is what I have
tbe table looks like
Product | StockCode | QtySold
Product A - Local | prdA001loc | 100
Product A - Imported | prdAImp7Z4 | 150
SELECT Product, SUM(QtySold) FROM tblA GROUP BY StockCode, Product
But this will just return the table as is. I would like this output:
Product | QtySold
Product A | 250
I believe that you need to update your DB schema to have reflect this information however if you need some naive solution you can use the following statement
SELECT substring(product, 1 , charindex('-',product)), SUM(QtySold)
FROM tblA GROUP BY substring(product, 1 , charindex('-',product))
note that the above statement assuming that all your products name will be similar to what is mentioned inside your question
This is a simple query problem. But it seems that I can't get it right :(
I just started using cakephp last week. I'm still playing around exploring. Anyway, here's my problem.
This is the relationship in Model: Product has many Stock. Stock belongs to Product.
This is the sample STOCKS table:
ID | Product Name | Transaction
------------------------------------
1 | Astringent | Purchase
2 | Glutathione | Sales
3 | Glutathione | Sales
I would like to get the number of transaction per product from the STOCKS table.
This is the output I would like with distinct product name:
Transaction | Astringent | Glutathione
--------------------------------------------
purchase | 1 | 0
sales | 0 | 2
Here is a sql query- for exactly what you requested.
SELECT transaction,
SUM(
CASE
WHEN product_name = 'Astringent' THEN 1
ELSE 0
END) AS 'Astringent',
SUM(
CASE
WHEN product_name = 'Glutathione' THEN 1
ELSE 0
END) AS 'Glutathione'
FROM stock
GROUP BY transaction;
However, are you looking for a pivot table i.e., an output with a variable number of columns based on a random number of products? If so, your solution is lot more complicated as a MySQL query, and potentially less complicated as a MySQL and PHP solution. In either case, I think you should share more of your database schema / CakePHP model along with some controller information. In order to render a view with this table is not overly difficult as long as you want Cakephp to output this format.
I have products. Each product is made up of items and assemblies. Assemblies themselves can be made up of items too. So it's a hierarchy but limited in depth. What I would like to do is list products with the items and assemblies it contains, plus any items in the product's assemblies.
This is the output I would like to see. It doesn't have to look exactly like this, but the aim is to show the items in the product, then the assemblies and within each assembly the items with in it. The number of columns isn't fixed, if more are necessary to show the items in the assemblies there is no problem with that.
ProductID ProductName AssemblyID AssemblyName ItemID ItemName
--------- ----------- ---------- ------------ ------ --------
P0001001 Product One
I0045 Item A
I0082 Item B
A00023 Assembly 1
I0320 Item 1
I0900 Item 2
A00024 Assembly 2
I0877 Item 3
I0900 Item 2
I0042 Item 4
This I can then use to build a report grouped on the Product ID to list the contents of each product.
This is the table structure I have at the moment.
+ProductList-+ +ProductItems-+
|ProductID | ----------> |ProductID | +ItemList-+
|ProductName | \ |ItemID | --------------------------------> |ItemID |
|Price | \ +-------------+ > |ItemName |
+------------+ \ / |Cost |
\ +ProductAssemblies-+ / +---------+
\-> |ProductID | +AssemblyItems-+ /
+-- |AssemblyID | ----> |AssemblyID | /
| |BuildTime | |ItemID | ---/
| +------------------+ +--------------+
|
| +AssemblyList-+
+-> |AssemblyID |
|AssemblyName |
+-------------+
What kind of SELECT statement would I need to do this.
I think I need some sort of outer join but I'm not totally up on SQL syntax to know how to structure the select statement. All my efforts have always led to the product being listed multiple times for each item and assembly. So if a product has 3 items and 2 assemblies, the product appears with 6 times.
Searching for this kind of problem is not easy as I don't know what I need to search on. Is it a three table problem, an outer join issue, or just a simple syntactical answer.
Or would it be better to switch to a pure hierarchical table structure without the use of assemblies? It would then be easier to search on hierarchical tables to solve any problems I might have.
I'm using LibreOffice 3.5.6.2 Base. It has wizards and other helpful things but they don't extend to the complexity of the situation that I find myself in. The aim is that the database contains prices and it can be used to properly price out the products from the cost of the items and time to build the assemblies.
Be gentle, I'm a newbie to SO.
The normal SQL approach to this would put all the data on one line, rather than split among several lines. So, your data would look like:
ProductID ProductName AssemblyID AssemblyName ItemID ItemName
--------- ----------- ---------- ------------ ------ --------
P0001001 Product One I0045 Item A
P0001001 Product One I0045 Item B
P0001001 Product One A00023 Assembly 1 I0320 Item 1
P0001001 Product One A00023 Assembly 1 I0320 Item 2
. . .
The product and assembly information, for instance, would not be blank for a given item. All would be on the same line.
This information comes from two sources, the product items and the assembly items. The following query gets each component, then unions them together, finally ordering the results by product:
select *
from ((select p.Productid, p.ProductName, NULL as AssemblyId, NULL as AssemblyName, il.Itemid, il.ItemName
from Product p join
ProductItems pi
on p.productId = pi.ProductId join
ItemList il
on pi.ItemId = il.ItemId
) union all
(select p.Productid, p.ProductName, al.AssemblyId, al.AssemblyName, il.Itemid, il.ItemName
from Product p join
ProductAssemblies pa
on pa.ProductId = p.ProductId join
AssemblyList al
on pl.AssembyId = al.AssemblyId, join
AssemblyItems ai
on al.AssemblyItems join
ItemList il
on p.ItemId = il.ItemId
)
) t
order by 1, 2, 3, 4, 5, 6
Often, restructuring into the format you want would be done at the app level. You can do it in SQL, but the best approach depends on the database you are using.