Update using a subquery with aggregates and groupby in Postgres - sql

I'm trying to update a column in a table with the max value of that column grouped by another column.
So for example, say we have a table named transactions with two columns: quantity and item_name. And for whatever reason we want to set quantity equal to the maximum quantity found for each item_name.
I'm pretty stumped and bad at doing things like this in SQL, but here's what I have so far:
UPDATE transactions
SET
quantity = subquery.quantity
FROM (select max(quantity), item_name
from transaction group by item_name) AS subquery
WHERE and item_name = subquery.item_name;

In addition to your syntax errors that #Gordon already pointed out, it is regularly a good idea to exclude empty updates:
UPDATE transaction t
SET quantity = sub.max_quantity
FROM (
SELECT item_name, max(quantity) AS max_quantity
FROM transaction
GROUP BY 1
) sub
WHERE t.item_name = sub.item_name
AND t.quantity IS DISTINCT FROM sub.max_quantity;
No need to write new row versions (at almost full cost) without changing anything. (Except if you want to fire a trigger.)

You are actually quite close:
UPDATE transactions
SET quantity = subquery.quantity
FROM (select max(quantity) as quantity, item_name
from transactions
group by item_name
) subquery
WHERE transactions.item_name = subquery.item_name;
I just removed the and in where and and renamed the table in the subquery to transactions.

Try this, it should also work for you. It would evaluate the quantity for each and every row and may be slower than Gordon's answer.
UPDATE transactions
SET quantity = (SELECT MAX(quantity)
FROM transaction as t
WHERE t.item_name = transactions.item_name)

Related

SQL oracle How can i multiply two columns from different tables and put the resulting column in one of the two tables

I am currently working on an SQL project and my code just won't work this is what I tried
UPDATE detail_order
SET total = (
SELECT (detail_order.quantity*product.PRICE_EXCLVAT)
FROM detail_order,product
where detail_order.npro=product.npro
);
But oracle keeps telling me: ora-01427 single-row subquery returns more than one row. But I can't figure out how to solve this because each product has one price and each order is linked to only one product so no confusion there. Some products are ordered several times but I can't see how this could be a problem.
Should probably be something like this:
update detail_order d set
d.total = (select d.quantity * p.price_exclvat
from product p
where p.npro = d.npro
)
where exists (select null from product a
where a.npro = d.npro
);
Or
merge into detail_order d
using product p
on (p.npro = d.npro)
when matched then update set d.total = d.quantity * p.price_exclvat;
None of these will work (i.e. you'll again get too_many_rows if there are more than a single row in the product table whose npro matches detail_order's npro.
What to do then? It depends on you; we don't have your tables and we don't know what the requirement is.
I believe what you want is something like this:
update detail_order d
set total = quantity *
(select price_exclvat from product where npro = d.npro)
;
quantity is the quantity from the same row you are updating. There is no reason to reference detail_order to retrieve the quantity for that row.

Counting the articles which don't have a price change in SQL Server

I have a table with 5 columns: suppliercode, articlecode, price, date, ArticleChanges. All I want is to count the articles where the price didn't change (the result will be on a additional column - ArticleChanges)
For instance:
For supplier 12345 I will have 1 article from a total of 2 articles which didn't change the price.
The idea is to aggregate by suppler and article. Then, you can compare the minimum and maximum prices:
select suppliercode, articlecode
from t
group by suppliercode, articlecode
having min(price) = max(price);
You can either do as Gordon has suggested and use the 'Having' statement, which can potentially impact performance as it is quite a heavy query, or you could do something like the following:
Select * From (
Select
Min(Price) as Minimum,
Max(Price) as Maximum,
SupplierCode,
ArticleCode
From
t
Group by
SupplierCode,
ArticleCode) MainQuery
Where MainQuery.Minimum = MainQuery.Maximum
See which one performs better as I'm sure there are cases where either one performs better.

How can i SUM records from a table to another after multiplying two columns

I have a table called orderItems which has two columns, quantity and unit price.It also has a foreign key ordernumber in that very table.
I have another table called ordergroup with primary key ordernumber, which contains SavedTotal column which is the order total based on quantity * unit price for all order item rows that reference that ordernumber.
Now what i struggle with is the sql query that can get all order items based on a certain ordernumber and calculate the total cost.
I have managed to do the multiplication but i am missing the SUM, here is my sql query(based on SQL Server) so far.
UPDATE OrderGroupNew
set OrderGroupNew.SavedTotal = OrderItemNew.UnitPrice*OrderItemNew.QUANTITY
FROM OrderItemNew
inner join OrderGroupNew on OrderItemNew.OrderNumber=OrderGroupNew.OrderNumber
any help is appreciated
UPDATE OrderGroupNew
SET SavedTotal = (
SELECT SUM(UnitPrice * Quantity)
FROM OrderItemNew
WHERE OrderNumber = OrderGroupNew.OrderNumber
)
You can use a TVP as well :
;With o as (
select OrderItemNew.OrderNumber as OrderNumber,
SUM(OrderItemNew.UnitPrice*OrderItemNew.QUANTITY) as OrderSum
Group by OrderItemNew.OrderNumber)
UPDATE OrderGroupNew
set OrderGroupNew.SavedTotal = o.OrderSum
FROM o
INNER JOIN OrderGroupNew on o.OrderNumber=OrderGroupNew.OrderNumber
Well the 1st answer is correct too. Choose the best in term of performance :) (dont know which will be the best, to be honest ! )

simple SQL update query error that i have no idea how to solve

I have two tables:
PS(size(primary key), price)
Sales(....size,quantity, total_Price)
I tried to execute this statement:
update Sales
set total_Price = (select price from PS, Sales
where Sales.size = PS.size )*Quantity;
but i get this error always
SQL0811N The result of a scalar fullselect, SELECT INTO statement, or
VALUES INTO statement is more than one row.
I know that the error is because the sub query i used in the select statement returns more than one row, that's why i can't work. Can someone please help me solve this problem.
ASSUMING size in PS is UNIQUE
update Sales
set total_Price = (SELECT max(PS.price) FROM PS PS
INNER JOIN Sales S on S.Size = PS.Size)*Quantity;
The problem I think is that your sales table contains multiple entries per size. Thus it's duplicate the price in the PS table. You either need the max price from that table, or the distinct value ASSUMING size is a unique value in the PS table. If size is NOT unique in the PS table then I would need to rethink what you're trying to do.

SQL "GROUP BY" issue

I'm designing a shopping cart. To circumvent the problem of old invoices showing inaccurate pricing after a product's price gets changed, I moved the price field from the Product table into a ProductPrice table that consists of 3 fields, pid, date and price. pid and date form the primary key for the table. Here's an example of what the table looks like:
pid date price
1 1/1/09 50
1 2/1/09 55
1 3/1/09 54
Using SELECT and GROUP BY to find the latest price of each product, I came up with:
SELECT pid, price, max(date) FROM ProductPrice GROUP BY pid
The date and pid returned were accurate. I received exactly 1 entry for every unique pid and the date that accompanied it was the latest date for that pid. However, what came as a surprise was the price returned. It returned the price of the first row matching the pid, which in this case was 50.
After reworking my statement, I came up with this:
SELECT pp.pid, pp.price, pp.date FROM ProductPrice AS pp
INNER JOIN (
SELECT pid AS lastPid, max(date) AS lastDate FROM ProductPrice GROUP BY pid
) AS m
ON pp.pid = lastPid AND pp.date = lastDate
While the reworked statement now yields the correct price(54), it seems incredible that such a simple sounding query would require an inner join to execute. My question is, is my second statement the easiest way to accomplish what I need to do? Or am I missing something here? Thanks in advance!
James
The reason you get an arbitrary price is that mysql cannot know which columns to select if you GROUP BY something. It knows it needs a price and a date per pid and can fetch the latest date as you requested with max(date) but chooses to return a price that is most efficient for him to retrieve - you didn't provide an aggregate function for that column (your first query is not valid SQL, actually.)
Your second query looks OK, but here is a shorter alternative:
SELECT pid, price, date
FROM ProductPrice p
WHERE date = (SELECT MAX(date) FROM ProductPrice tmp WHERE tmp.pid = p.pid)
But if you access the latest price a lot (which I think you do), I would recommend adding the old column back to your original table to hold the newest value, if you have the option of altering the database structure again.
I think you broke your database schema.
To circumvent the problem of old invoices showing inaccurate pricing after a product's price gets changed, I moved the price field from the Product table into a ProductPrice table that consists of 3 fields, pid, date and price. pid and date form the primary key for the table.
As you have pointed out you need to keep a change history of prices. But you can still keep the current price in the products table in addition to that new table. That would make your life much easier (and your queries faster).
You cannot solve your problem with the GROUP BY clause, because for each group of pid MySQL will simply fetch the first pid, the maximum date and the first price found (which is not what you need).
You may either use a subquery (which can be inefficient):
SELECT pid, date, price
FROM ProductPrice p1
WHERE date = ( SELECT MAX(p2.date)
FROM ProductPrice p2
WHERE p1.pid = p2.pid)
or you can simply join the table with itself:
SELECT p1.pid, p1.date, p1.price
FROM ProductPrice p1
LEFT JOIN ProductPrice p2 ON p1.pid = p2.pid
AND p1.date < p2.date
WHERE p2.pid IS NULL
Take a look at this section of MySQL docs.
You might wanna try this:
SELECT pid, price, date FROM ProductPrice GROUP BY pid ORDER BY date DESC
Group has some obscure functionality, I'm too always unsure if it's the right field...but it should be the first in the resultset.
Here is another -possibly inefficient- one:
SELECT pid, substring_index( group_concat( price order by date desc ), ',', 1 ) , max(date)
FROM ProductPrice
GROUP BY pid
I think that the key here is simple sounding query - you can see what you want but computers ain't human and so to produce the desired result from set based operations you have to be explicit as in the second query.
The inner query identifies the last price for each product, then the outer query lets you get the value for the last price - that's about as simple as it can get.
As an aside, if you have an invoicing system, you really ought to store the price for the product (and the tax rates as well as the "codes") with the invoice i.e. the invoice tables should contain all the necessary financial information to reproduce the invoice. In general, you do not want to rely on being able to look up a price (or a tax rate) in a mutable table even allowing for the system introduced as above. Regardless of this have the pricing history has its own merits.
i faced same problem in one of my project i used subquery to fetch date and then compare it but it makes system slow when data increases. so, its better to store latest price in your Products table in addition to the new table you have created to keep history of price changes.
you can always use any of query ppl suggested to get latest price of product on particular date. but also you can add one field in the same table is it latest. so for one date you can make flag true once. and you can always find product's latest price for particular date by one simple query.