I have a table containing items, each item belongs to an store, e.g:
id | name | store_id | price
1 | hat | 1 | 110
2 | bag | 1 | 120
3 | coat | 2 | 130
A Store can be canonical or a duplicate. A canonical Store has canonical_id equal to null, and a duplicate Store has canonical_id equal to the ID of the canonical Store, e.g:
id | name | canonical_id
1 | NYC | null
2 | Bronx | 1
I need to group items by their Store to get the total stock value of all items at the store, e.g:
SELECT store_id, SUM(price) as `stock_value` FROM items GROUP BY store_id
This would produce 2 results, Store 1 has a stock_value of 230 and Store 2 has a stock_value of 130.
Because Store 2 is a duplicate of Store 1 the items from Store 2 should be included in the total for Store 1. The goal is for this example to provide a single result of 360.
I think the correct implementation would involve some sort of join which retrieves the Store ID from stores by using IFNULL to get either the canonical_id or id after selecting based on id from the items table, but I'm struggling to implement a solution that works.
Any pointers would be greatly appreciated, thank you.
Edit: my attempt is as follows, it appears to meet my needs, are there any caveats / issues with my approach?
SELECT SUM(price) as `stock_value`, IFNULL(stores.canonical_id, store_id) as `store`
FROM items
JOIN stores on stores.id = items.store_id
GROUP BY store
I just realize you don't wanted to keep the stock_values of the "children" or "related" stores. However the next approach take those into account too:
SELECT
s.*,
(SELECT
SUM(i.price)
FROM
items AS i
INNER JOIN
stores AS s1 ON s1.id = i.store_id
WHERE
s1.cannonical_id = s.id
OR
s.id = i.store_id) AS "stock_value"
FROM
stores AS s
Online example: DB-Fiddle
If you don't want they, you just could filter the previous query using the condition WHERE s.cannonical_id is NULL like this:
SELECT
s.*,
(SELECT
SUM(i.price)
FROM
items AS i
INNER JOIN
stores AS s1 ON s1.id = i.store_id
WHERE
s1.cannonical_id = s.id
OR
s.id = i.store_id) AS "stock_value"
FROM
stores AS s
WHERE
s.cannonical_id is NULL
But, you should note that the query you posted on the updated question will be better in performance than this approach.
Related
I've been going around this for a while and there isno way I can figure it out. Let's say I have these three tables:
Users
Name | Basket value
-------+---------------
John | ???
-------+---------------
Pierre | ???
Items
User | Item | Amount
-----------+------------+------------
John | Pears | 2
-----------+------------+------------
Pierre | Pears | 1
-----------+------------+------------
Pierre | Apples | 3
Market_ prices
Item | Price
------------+---------------
Pears | 2.35
------------+---------------
Apples | 2.56
Basket value is needed. So for each row in ITEMS it must multiply its AMOUNT by MARKET_PRICES[PRICE] and sum all the results grouped by USER and place this result in USERS[total items value]. But how could the syntax be elaborated to take this to practice?
Many thanks in advance for the help.
One approach is to join the relevant tables:
SELECT u.name, SUM(i.amount*m.price)
FROM users u
JOIN items i ON u.name = i.user
JOIN market_prices m ON i.item = m.item
GROUP BY u.name;
(SQLfiddle of this is at: http://sqlfiddle.com/#!9/ec224/6 - I added a few other rows to the tables to test more complexity, so the totals aren't what you'd get from your example. Specifically, I added Bananas 3.75 to the Market_prices table and John Apples 3 and Pierre Bananas 5 to the Items table.)
The goal here is to link the information in all three tables through shared fields (via the JOIN), while also GROUPing and creating a SUM of the calculated costs as a product of market price and number of items.
---- edited based on comments ----
To do this as an update, you could try:
UPDATE users
SET basket_value = (SELECT basket FROM
(SELECT name, SUM(i.amount*m.price) AS basket
FROM users u JOIN items i ON u.name = i.username
JOIN market_prices m ON i.item = m.item
GROUP BY u.name) q1 WHERE q1.name = users.name);
I have a feeling there is a more elegant solution, but this works. Modified SQLFiddle is: http://sqlfiddle.com/#!9/56245a/1
SELECT User, SUM(rev) AS basket_price
FROM
(
SELECT a.User AS User, a.Amount*b.Price AS rev
FROM Items a
LEFT JOIN
Market_Prices b
ON a.Item = b.Item
) a1
GROUP BY User
I have two tables, bills and linesbill. I need all the products that a customer has ever bought. I've gotten this to work:
SELECT referencia, codcliente, pvpunitario, t2.fecha FROM
lineasfacturascli T1
INNER JOIN facturascli T2 ON T1.idfactura = T2.idfactura
WHERE T2.codcliente = "000001"
GROUP BY referencia
But I need get the last price that the customer has paid for each product. I'm trying to order by "fecha"->(date) but it does not work.
Tables structure
facturascli
idfactura(id bill),
codcliente(client id),
fecha(date)
lineasfacturascli
referencia(name of product),
idfactura(id bill)
pvpunitario(price)
Edit
DRapp solution works but I also need to handle the case that a customer buys it in the same day get only the lower price:
With the solution provided the result is:
|Referencia| |MostRecentDatePerItem| |MostRecentPricePerItem|
| pendrive | | 2017-03-02 | | 50 |
| pendrive | | 2017-03-02 | | 10 |
| samsung | | 2017-03-02 | | 50 |
| linux car| | 2017-04-26 | | 9.99 |
I need:
|Referencia| |MostRecentDatePerItem| |MostRecentPricePerItem|
| pendrive | | 2017-03-02 | | 10 |
| samsung | | 2017-03-02 | | 50 |
| linux car| | 2017-04-26 | | 9.99 |
Thanks
I would start with an inner pre-query of all line items for a specific person with a max date per item as a group by. So if a person ordered the 10 things multiple times over say... 50 orders, you would still have the final list of 10 things, but also the most recent date the thing was ordered.
The following is based on not exactly knowing your structures, nor sample data (please provide for future). Also, you should always qualify your table columns in a query with the corresponding table alias reference so users know which field comes from what table. I have to assume the "pvpunitario" column is from the line item details as the price, but basic translation appears to be "unit" not price. You will have to adjust accordingly if I am inaccurate on my impression.
select
T1.referencia,
max( t2.fecha ) as MostRecentDatePerItem
FROM
lineasfacturascli T1
INNER JOIN facturascli T2
ON T1.idfactura = T2.idfactura
WHERE
T2.codcliente = "000001"
GROUP BY
T1.referencia
So this will give us just the products and the maximum date ever ordered by a single client. Now, we take this result as a basis to the original query, re-joined to the line items / order headers that specifically match the corresponding MostRecentDatePerItem.
select
TT1.Referencia,
PQ.MostRecentDatePerItem,
TT1.pvpunitario as MostRecentPricePerItem
from
lineasfacturascli TT1
JOIN
(select
T1.referencia,
max( t2.fecha ) as MostRecentDatePerItem
FROM
lineasfacturascli T1
INNER JOIN facturascli T2
ON T1.idfactura = T2.idfactura
WHERE
T2.codcliente = "000001"
GROUP BY
T1.referencia ) PQ
on TT1.Referencia = PQ.Referencia
JOIN facturascli TT2
ON TT1.idfactura = TT2.idfactura
AND PQ.MostRecentDatePerItem = TT2.Fecha
where
TT2.codcliente = "000001"
To clarify what is going on. The inner query (now alias "PQ" -- PreQuery), is just those qualifying items for the one client in question with the most recent date said item was purchased.
So now back to the original list of all order line items joined to this table keeps the reference product ID linked. Now, we go again to the order header table and still apply the same client code, but ALSO joined on the same FETCHA date as the maximum date found for the transaction. So only THEN do we want to grab the detail level price / unit information for said product.
Hopefully this helps direct your final solution. If I am incorrect on any pieces, you should EDIT your original question and supply the additional missing details / alias references / sample data. Then you can reply comment for follow-up support.
Answer per Comment.
To get the minimum price, you would just adjust the outer select and add a group by. Since the item is the same, the group by will only group for the prices on that specific day. Change the above to...
select
TT1.Referencia,
PQ.MostRecentDatePerItem,
MIN( TT1.pvpunitario ) as LeastPricePerItemOnThisDate
(same rest of query)
GROUP BY
TT1.Referencia,
PQ.MostRecentDatePerItem
I am making queries to extract data from database which holds customer order. There's one table which holds customer id's and the customer's name. Another table which has the order id, customer id of who placed the order, a quantity of the item bought, and an item id. The last table holds the item id's and item names. I am trying to sort these to show an individual's most popular purchase, but am having issues properly grouping and ordering to produce the correct result, below is an example of what is intended.
customers
1 | John
---+-----
2 | Jane
orders
1 | 2 | 4 | 1
---+---+---+---
2 | 2 | 5 | 2
---+---+---+---
3 | 2 | 2 | 1
---+---+---+---
4 | 1 | 1 | 2
items
1 | Chair
---+-------
2 | Sofa
After properly sorting and grouping, the output table should like:
John | Sofa
------+------
Jane | Chair
Currently I can connect the item names to the purchaser and return a random item bought, but not the most popular by quantity. I have tried entering multiple fields into group by and managed to properly group the items by name and sort by quantity, but in doing so the customer id's became ungrouped. Been trying to solve this for days so any help would be appreciated. Please note that this is a very simplified version of the actual problem where many more tables are involved, including multiple items table which are being joined together to one.
You should use group by on joined tables
select
b.name
, c.name
, sum(quantity) as tot
from orders as a
inner join Customers as b on a.customer_id = b.id
inner join Items as c on a.item_id = c.id
group by b.name, c.name
order by tot
Selecting the sum of the quantities per customer-item group is easy, but selecting the top seller is a bit harder.
The first step is the query to get all the groups with the sums of the quantities for each customer-item:
SELECT
customer_name,item_name,SUM(quantity)
FROM
orders o
JOIN customers c ON o.customer_id=c.id
JOIN items i ON o.item_id=i.id
GROUP BY customer_name,item_name;
Then to only select the groups with the maximum quantity sums we use some trickery:
SELECT
customer_name,item_name,SUM(quantity),
(SELECT SUM(quantity) AS qmax
FROM
orders o2
JOIN customers c2 ON o2.customer_id=c2.id
JOIN items i2 ON o2.item_id=i2.id
WHERE c2.id=c.id
GROUP BY c2.customer_name,i2.item_name
ORDER BY qmax DESC LIMIT 1) AS qmax
FROM
orders o
JOIN customers c ON o.customer_id=c.id
JOIN items i ON o.item_id=i.id
GROUP BY customer_name,item_name
HAVING SUM(quantity)=qmax;
Edit:
Here's a link to a fiddle: SQLFiddle
I have stumped all the IT people at my work with this one, so wondeirng if anyone can help.
I need to extract from an order table anyone who has only purchased a specific product type, (if they have order the product type and any other product types i dont want to know who you are)
for example the table is roughly
---------------------------------------------------------------------------------------
Order ID | item code | Name |
----------------------------------------------------------------------------------------
1 | ADA | item 1
2 | ADA | item 1
2 | GGG | item 2
3 | ADA | item 1
----------------------------------------------------------------------------------------
So i want to find all the order IDs of people who only purchased item code ADA, BUT not if they purchased over items, so the output of this query should be order ID 1 & 3 and skipping order 2 as this had a different item.
Would really appriciate it if anyone could help.
Assuming an order can't have multiple records with the same ItemCode, you could use:
SELECT *
FROM Orders
WHERE OrderID IN (
SELECT OrderID
FROM Orders
GROUP BY OrderID HAVING COUNT(*) = 1
)
AND ItemCode = 'ADA'
If an order could have multiple records with the same ItemCode then you'd have to change the SELECT * to SELECT DISTINCT * and then COUNT(*) to COUNT(DISTINCT ItemCode)
Based on your current explanation and example, the below should work. However, there are outstanding questions in the comments which may change the actual correct solution.
SELECT
O.OrderId, MAX(itemCode), MAX(Name)
FROM
Orders O
INNER JOIN
(SELECT
OrderId
FROM
Orders
WHERE
itemCode = 'ADA') ADA
ON
O.OrderId = ADA.OrderId
GROUP BY
O.OrderId
HAVING
COUNT(*) = 1
I have a join table named languages_services that basically joins the table services and languages.
I need to find a service that is able to serve both ENGLISH (language_id=1) and ESPANOL (language_id=2).
table languages_services
------------------------
service_id | language_id
------------------------
1 | 1
1 | 2
1 | 3
2 | 1
2 | 3
With the data provided above, I want to test for language_id=1 AND language_id=2 where the result would look like this
QUERY RESULT
------------
service_id
------------
1
Obviously it doesn't return the one with service_id=2 because it doesn't service Espanol.
Any tips on this is greatly appreciated!
SELECT
service_id
FROM
language_services
WHERE
language_id = 1
OR language_id = 2
GROUP BY
service_id
HAVING
COUNT(*) = 2
Or...
WHERE
lanaguage_id IN (1,2)
GROUP BY
service_id
HAVING
COUNT(*) = 2
If you're always looking at 2 languages you could do it with joins, but the aggregate version is easier to adapt to differing numbers of language_ids. (Add an OR, or add an item to the IN list, and change the COUNT(*) = 2 to COUNT(*) = 3, etc, etc).
Be aware, however, that this scales very poorly. And with this table structure there isn't much you can do about that.
EDIT Example using a join for 2 languages
SELECT
lang1.service_id
FROM
language_services AS lang1
INNER JOIN
language_services AS lang2
ON lang1.service_id = lang2.service_id
WHERE
lang1.language_id = 1
AND lang2.language_id = 2