how to select the lower value product of matching SKU - mysql

I have two suppliers where I am trying to filter the products to achieve the following:
Select Products from Table1(supplier 1) that are not already present in Table3 and some further filtering on categories (Working)
Select Products from Table2 (Supplier 2) that are not already present in Table3 (Working)
There are duplicate products from Table1 and Table2. I want to filter these so the resulting table does not have duplicate products but I want to do this by selecting the cheaper supplier rate as the product that ends up in the table. (Stuck on this).
My current SQL query at the moment is the below which does the first two items but I am unsure how to modify to get the third item above working. Any suggestions greatly appreciated.
SELECT Vendor,
VendorStockCode AS SKU,
StockDescription AS Description,
StockAvailable AS Stock
FROM Table1
WHERE NOT EXISTS (SELECT sku
FROM Table3_product_meta_lookup
WHERE Table1.VendorStockCode = Table3_product_meta_lookup.sku)
AND PrimaryCategory != 'SERVICES'
AND PrimaryCategory != 'WARRANTY'
AND cast(DealerEx as decimal(10,2)) <= cast('15000.00' as decimal(10,2))
UNION
SELECT Manufacture_Name,
Manufacture_Code,
Short_Description,
Stock_Qty
FROM Table2
WHERE NOT EXISTS (SELECT sku
FROM Table3_product_meta_lookup
WHERE Manufacture_Code = Table3_product_meta_lookup.sku)

OK, after some experiment, I followed a similar line to what I had and seems to be working. Not sure if it is the most efficient manner but is getting the data I am after. Thank you for those who responded. I have pasted the final query below for reference:
SELECT Vendor, VendorStockCode AS SKU,
StockDescription AS Description,
StockAvailable AS Stock
FROM Table1
WHERE NOT EXISTS (
SELECT sku
FROM Table3_product_meta_lookup
WHERE Table1.VendorStockCode = Table3_product_meta_lookup.sku )
AND PrimaryCategory != 'SERVICES'
AND PrimaryCategory != 'WARRANTY'
AND CAST(DealerEx AS DECIMAL(10,2)) <= CAST('15000.00' AS DECIMAL(10,2))
AND NOT EXISTS (
SELECT Manufacture_Code
FROM Table2
WHERE VendorStockCode = Manufacture_Code
AND CAST(DealerEx AS DECIMAL(10,2)) >= CAST(ExTax AS DECIMAL(10,2)))
UNION
SELECT Manufacture_Name, Manufacture_Code, Short_Description,
Stock_Qty
FROM Table2
WHERE NOT EXISTS (
SELECT sku
FROM Table3_product_meta_lookup
WHERE Manufacture_Code = Table3_product_meta_lookup.sku )
AND NOT EXISTS (
SELECT VendorStockCode
FROM Table1
WHERE VendorStockCode = Manufacture_Code
AND CAST(DealerEx AS DECIMAL(10,2)) < CAST(ExTax AS DECIMAL(10,2)));

How to calculate supplier rate?
You want to remove the duplicate products and leave one which is lower in supplier rate?
Assuming that you've already collected all data from table1 and table2 with supplier_rate column and union them into a table 'table_a'.
It will be easier to filter the duplicated SKUs with different suppliers and leave on with one supplier with a lower rate.
The tested query below.
select vendor,sku,stock from (
select vendor,sku,stock,supplier_rate,rank() over(PARTITION by sku order by supplier_rate) as rk from table_a) pd
where rk = 1;
The input table data:
vendorname sku stock supplier_rate
vendor1 100 1000 30
Manufacture1 100 2000 40
vendor3 200 1500 50
Manufacture2 300 2000 60
Manufacture3 200 1200 25
The output table data:
vendorname sku stock
vendor1 100 1000
Manufacture3 200 1200
Manufacture2 300 2000

Related

MYSQL - Group By / Order By not working

I have the following data inside a table:
id person_id item_id price
1 1 1 10
2 1 1 20
3 1 3 50
Now what I want to do is group by the item ID, select the id that has the highest value and take the price.
E.g. the sum would be: (20 + 50) and ignore the 10.
I am using the following:
SELECT SUM(`price`)
FROM
(SELECT id, person_id, item_id, price
FROM `table` tbl
INNER JOIN person p USING (person_id)
WHERE p.person_id = 1
ORDER BY id DESC) x
GROUP BY item_id
However, this query is still adding (10 + 20 + 50), which is obviously not what I need to have.
Any ideas to where I am going wrong?
Here is what you are trying to achieve. First you need grouping in a subquery and not in outer query. In outer query you need only sum:
SELECT SUM(`price`)
FROM
(SELECT MAX(price) as price
FROM `table` tbl
INNER JOIN person p USING (person_id)
WHERE p.person_id = 1
GROUP BY item_id) x
http://sqlfiddle.com/#!9/40803/5
SELECT SUM(t1.price)
FROM tbl t1
LEFT JOIN tbl t2
ON t1.person_id= t2.person_id
AND t1.item_id = t2.item_id
AND t1.id<t2.id
WHERE t1.person_id = 1
AND t2.id IS NULL;
I'm not sure if this is the only requirement you have. If so, try this.
SELECT SUM(price)
FROM
(SELECT MAX(price)
FROM table
WHERE person_id = 1
GROUP BY item_id)
First of all - you don't need the person table, because the other table already contains the person_id. So i removed it from the examples.
Your query returns a sum of prices for each item.
If you replace SELECT SUM(price) with SELECT item_id, SUM(price) you wil get
item_id SUM(`price`)
1 30
3 50
But that is not what you want. Neither is it what you wrote in the question " (10 + 20 + 50)".
Now replacing the first line with SELECT id, item_id, SUM(price) you will get one row for each item with the highest id.
id item_id price
2 1 20
3 3 50
This works because of the "undocumented feature" of MySQL, wich allows you to select columns that are not listed in the GROUP BY clause and get the first row from the subselect each group (each item in this case).
Now you only need to sum the price column in an additional outer select
SELECT SUM(price)
FROM (
SELECT id, item_id ,price
FROM (
SELECT id, person_id, item_id, price
FROM `table` tbl
WHERE tbl.person_id = 1
ORDER BY id DESC ) x
GROUP BY item_id
) y
However i do not recomend to use that "feature". While it still works on MySQL 5.6, you never know if that will work with newer versions. It already doesn't work on MariaDB.
Instead you can determite the MAX(id) for each item in an subselect, select only the rows with the determined ids and get the summed price of them.
SELECT SUM(`price`)
FROM `table` tbl
WHERE tbl.id IN (
SELECT MAX(tbl2.id)
FROM `table` tbl2
WHERE tbl2.person_id = 1
GROUP BY tbl2.item_id
)
Another solution (wich internaly does the same) is
SELECT SUM(`price`)
FROM `table` tbl
JOIN (
SELECT MAX(tbl2.id) as id
FROM `table` tbl2
WHERE tbl2.person_id = 1
GROUP BY tbl2.item_id
) x ON x.id = tbl.id
Alex's solution also works fine, if the groups (number of rows per person and item) are rather small.
You have used group by in main query, but it is on subquery like
SELECT id, person_id, item_id, SUM(`price`) FROM ( SELECT MAX(price) FROM `table` tbl WHERE p.person_id = 1 GROUP BY item_id ) AS x

SQL - How to use COUNT() with more than one table

I have a table that includes an COUNT() function that returns all the fields and counts them out. Now, I need to join another table and return both the number of rows that match and the rows that do no match. I am not exactly sure how I can use the GROUP BY and COUNT() function and get the results I need.
SELECT tb1.type, count(tb1.type) as total
FROM table1 tb1
GROUP BY tb1.type
That will return the totals for me:
RESULTS for TABLE1:
Type total
Egg 200
Cream 133
Milk 12
That's great. However, now I have another table...table 2 that has some records that match so doing that same query on that table will show something like:
RESULTS for TABLE2:
Type total
Egg 187
Cream 103
Milk 6
So, my results will need to look like this:
RESULTS for TABLE1:
Type total totalINTABLE2 totalNOTINTABLE2
Egg 200 187 13
Cream 133 103 30
Milk 12 6 6
Not sure if joining is best here, or a UNION, or EXISTS. What's getting me confused is the group by.
Well, you can simply JOIN both the resultset like
SELECT tb1.type,
tb1.total,
tb2.totalINTABLE2,
(tb1.total - tb2.totalINTABLE2) as totalNOTINTABLE2
FROM (
SELECT type, count(type) as total
FROM table1
GROUP BY type) tb1
JOIN (
SELECT type, count(type) as totalINTABLE2
FROM table2
GROUP BY type) tb2 ON tb1.type = tb2.type;
Use a CTE to calculate the total in each table then join them together:
;WITH
cte1 as (
SELECT tb1.type, count(tb1.type) as total
FROM table1 tb1
GROUP BY tb1.type
),
cte2 as (
SELECT tb2.type, count(tb2.type) as total
FROM table2 tb2
GROUP BY tb2.type
)
SELECT ISNULL(cte1.type, cte2.type) as type,
cte1.total as TotalInTable1,
cte2.total as TotalInTable2,
ISNULL(cte1.total, 0) - ISNULL(cte2.total, 0) as TotalNotInTable2
FROM cte1
FULL JOIN cte2 ON cte1.type = cte2.type

MySql - Get duplicates by multiple columns

I have an part inventory table that stores parts by PartName, WarehouseId, VendorCode (main interest columns). It should only have unique PartName entries by WarehouseId and VendorCode. However, entries are mixed, and I need to get the PartName, WarehouseId and Vendor for such a case. E.g:
ABC133, Warehouse 10, VendorCode 1234
ABC133, Warehouse 10, VendorCode 1222
BBB111, Warehouse 20, VendorCode 1111
BBB111, Warehouse 20, VendorCode 2222
I have customized a query found on this site to do this, but it only brings the first "duplicate" for every duplicate PartName, and I need to get all the faulty entries:
ABC133, Warehouse 10, VendorCode 1222
BBB111, Warehouse 20, VendorCode 1111
This is the query I use:
SELECT i.MFGPN, i.VendorCode, i.WarehouseID FROM edi_846_inventory i
INNER JOIN (SELECT MFGPN FROM edi_846_inventory
GROUP BY MFGPN HAVING count(MFGPN) > 1 and count(VendorCode) > 1) dup ON i.MFGPN = dup.MFGPN
where MFGPN is the PartName
Thanks
This is the query that you want:
SELECT i.MFGPN, i.VendorCode, i.WarehouseID
FROM edi_846_inventory i INNER JOIN
(SELECT MFGPN, WarehouseID
FROM edi_846_inventory
GROUP BY MFGPN, WarehouseID
HAVING count(*) > 1
) dup
ON i.MFGPN = dup.MFGPN AND i.WarehouseID = dup.WarehouseID;
In other words, your subquery needs to aggregate by both MFGPN and WarehouseID.
Also, just concatenating the vendors together might be sufficient for you:
SELECT MFGPN, WarehouseID, GROUP_CONCAT(VendorCode) as Vendors
FROM edi_846_inventory
GROUP BY MFGPN, WarehouseID
HAVING count(*) > 1
A work colleague found a solution:
select i.MFGPN, i.WarehouseID, i.VendorCode, i.IngramSKU
from edi_846_inventory i
where i.MFGPN in
(
select *
from
(
select distinct i.MFGPN
from edi_846_inventory i
group by i.MFGPN, i.WarehouseID
having count(*) > 1
) dup
)
order by i.MFGPN, i.WarehouseID

Getting the largest value for each group in a table

So, I have a table that is something like this...
person | account | accountbalance
--------------------------------------------
1 a 100
1 b 250
1 c 283
2 a 25
2 b 199
3 a 65
and for each person, I need to find the account that has the highest balance. I am doing this right now:
SELECT person, account, accountbalance FROM mytable
AND accountbalance=
(SELECT MAX(accountbalance) FROM mytable);
But this only returns the ONE top account among them all, not for each person.
One way is to compute the max in a derived table and join with that:
SELECT mytable.person, account, accountbalance
FROM mytable
JOIN (
SELECT person, MAX(accountbalance) MaxAccountBalance
FROM mytable
GROUP BY person
) t ON mytable.person = t.person
AND mytable.accountbalance = t.MaxAccountBalance;
or you could do a correlated subquery in the where clause (which is what you almost did - you just missed the necessary correlation):
SELECT person, account, accountbalance
FROM mytable m1
WHERE accountbalance = (SELECT MAX(accountbalance) FROM mytable WHERE person = m1.person);
Sample SQL Fiddle
For MYSQL, you can try this way also
select *
from (select * from mytable order by `person`, accountbalance desc, account) x
group by `person`

MySQL sum() on different group bys

Ok, I have a query over two tables. I need to get two sums. I do a group by so the sum() works correctly.
SELECT sum(a.x), sum(b.y) FROM a,b GROUP BY a.n where a.n=b.m
So far this works well, but the problem is i need to group them differently for the second sum (sum(b.y)), than for the first sum (sum(a.x)).
The real query is somewhat more complex but this is my main problem.
This is what i actually try to select sum(stock.amount) - if( sold.amount IS NULL , 0, sum( sold.amount ) )
How can I solve that in one query?
since you are not writing down the tables I am gonna make a wild guess and assume the tables are like :
stock : id, item_id, amount
sold : id, item_id, amount
then again I assume that you need the stock_in_total, sold_total, left_total counts
SELECT
stock_sums.item_id,
stock_sums.st_sum as stock_in_total,
COALESCE(sold_sums.so_sum,0) as sold_total,
(stock_sums.st_sum - COALESCE(sold_sums.so_sum,0)) as left_total
FROM (
SELECT stock.item_id as item_id, SUM(stock.amount) as st_sum
FROM stock
GROUP BY item_id
) as stock_sums
LEFT JOIN (
SELECT sold.item_id as item_id, SUM(sold.amount) as so_sum
FROM sold
GROUP by item_id
) as sold_sums ON stock_sums.item_id = sold_sums.item_id
I hope this would help.
Here is how I would do it. I assume that Stock is the main table, with an ID and an amount, and that Sold maps to Stock via an ID value, and has zero to many records for each Stock item.
SELECT Q1.id, Q1.Total1, Q2.Total2
, Q1.Total1 - COALESCE(Q2.Total2,0) as Outstanding
FROM (
SELECT id, SUM(amount) as Total1
FROM Stock GROUP BY id
) as Q1
LEFT OUTER JOIN (
SELECT id, SUM(Amount) as Total2
FROM Sold GROUP BY id
) as Q2
ON Q2.id = Q1.id
Note that simply formatting your SQL into a clean way forces you to break it into logical parts and will often reveal exactly what is wrong with the query.
The example above also handles correctly the cases where there is not match in the Sold table.
Cheers,
Daniel
(Code Assumptions)
DROP TABLE Stock
CREATE TABLE Stock (
id integer
, amount decimal(10,2)
)
INSERT INTO Stock (id, amount ) VALUES ( 1, 10.1);
INSERT INTO Stock (id, amount ) VALUES ( 2, 20.2);
INSERT INTO Stock (id, amount ) VALUES ( 3, 30.3);
SELECT * FROM STOCK
DROP TABLE Sold
CREATE TABLE Sold (
id integer
, amount decimal(10,2)
)
INSERT INTO Sold (id, amount ) VALUES ( 1, 1.1);
INSERT INTO Sold (id, amount ) VALUES ( 1, 2.2);
INSERT INTO Sold (id, amount ) VALUES ( 1, 3.3);
INSERT INTO Sold (id, amount ) VALUES ( 2, 2.22);
SELECT * FROM Sold
SELECT Q1.id, Q1.Total1, Q2.Total2
, Q1.Total1 - COALESCE(Q2.Total2,0) as Outstanding
FROM (
SELECT id, SUM(amount) as Total1
FROM Stock GROUP BY id
) as Q1
LEFT OUTER JOIN (
SELECT id, SUM(Amount) as Total2
FROM Sold GROUP BY id
) as Q2
ON Q2.id = Q1.id
Results:
id Total1 Total2 Outstanding
1 10.10 6.60 3.50
2 20.20 2.22 17.98
3 30.30 30.30
REVISION
It sounds like you want the total amount of stock you have as one count for each different stock. Then you want how much stock you have left for each stock based on what has been sold. Correct?
If so check this out:
select stock, sum(a.x) as sharesBeforeSale, (sum(a.x) - sum(b.y)) as sharesAfterSale
FROM db.table1 a, db.table2 b
WHERE a.UNIQUEID = b.UNIQUEID AND b.y IS NOT NULL
GROUP BY a.UNIQUEID;
Does that accomplish what you are looking to do?
stock sharesBeforeSale sharesAfterSale
duk 100 25
orc 101 101
yrc 54 41
Enjoy!
Sample tables
db.table1 (stock owned):
UNIQUEID x stock
1 100 duk
2 101 orc
3 54 yrc
db.table2 (stock sold):
UNIQUEID y
1 75
2 0
3 13