Update based on the results of previous update statement - sql

We had a requirement to update price values in the Orders table based on the productids from the Products table. This is simple:
update o
set o.price = p.price
from Orders o
inner join Products p
on o.productid = p.productid
Then the ask was to update the price from a different table (OrderPricing) based on a different column (orderpricingid) for the orders that were not updated by the previous query.
So I have another update statement:
update o
set o.price = op.price
from Orders o
inner join OrderPricing op
on o.orderpricingid = p.orderpricingid
where o.price is null
So my question is - can the two update statements be combined together? Is there a way to update based on the results of the previous update query?

You can do both in the same query, if you change the joins from inner to left, using coalesce:
update o
set o.price = coalesce(p.price, op.price, o.price)
from Orders o
left join Products p on o.productid = p.productid
left join OrderPricing op on o.orderpricingid = p.orderpricingid
This will update the price from the products table if the product exists there, if not, it will use the price from the OrderPricing table, but if that also don't exists it will just keep the original price.

You can try this way, as if o.price is null then you will update from order price so we can use case when in time of update
update o
set o.price= case when p.price is null then op.price else p.price end
from Orders o
inner join Products p
on o.productid = p.productid
inner join OrderPricing op
on o.orderpricingid = p.orderpricingid

I believe you need a left join:
update o
set o.price = coalesce(p.price, op.price)
from Orders o left join
Products p
on o.productid = p.productid left join
OrderPricing op
on o.orderpricingid = p.orderpricingid;

Related

Update SQL Query with where condition

I added a column in the Orders table called EventId and I have to fill the column with the right value for every row.
I want to achieve it with a more complex sql query like below:
UPDATE [dbo].[Orders] o
SET o.EventId = ????
WHERE o.Id IN (SELECT o.id, s.eventid FROM orders o
INNER JOIN orderskudiscounts osd ON o.id = osd.orderid
INNER JOIN skus s ON osd.skuid = s.id GROUP BY o.id, s.eventid)
I not sure how can I write the query successfully... I have more than 2 thousand mapping to do. So I should use a query...
Thank for any help
I am guessing that you want an update like this:
UPDATE o
SET o.EventId = s.eventid
FROM orders o INNER JOIN
orderskudiscounts osd
ON o.id = osd.orderid INNER JOIN
skus s
ON osd.skuid = s.id;

Query returns cartesian product when not expected

Task: Select all orders having products belonging to ‘Sea Food’ category.
Result: OrderNo, OrderDate, Product Name
I write this query but it returns Cartesian products.
select o.orderid, o.orderdate as "Order Date", p.productname , ct.categoryname from orders o,
order_details od , products p , customers c ,categories ct
where
od.orderid = o.orderid and p.productid = od.productid and ct.categoryid = p.categoryid
and ct.categoryname = 'Seafood';
Question: What is wrong with my query ?
You're doing a CROSS JOIN on customers table since you forgot to specify the connection. This is why you should use explicit JOIN syntax rather than old syntax using commas in WHERE clause.
After translating your query into explicit syntax, you will see that there is no WHERE condition involving customers table:
select
o.orderid,
o.orderdate as "Order Date",
p.productname,
ct.categoryname
from
orders o,
inner join order_details od on od.orderid = o.orderid
inner join products p on p.productid = od.productid
inner join categories ct on ct.categoryid = p.categoryid
cross join customers c -- either you don't need this table, or you need to specify conditions
where
ct.categoryname = 'Seafood'
Basically the reason you got it was that your where clause omitted join condition involving customers table, so you were left with:
from (...), customers -- cross join when joining condition not applied in where clause

Update column with a subquery

ALTER TABLE order_t ADD Totalfixed DECIMAL(7,2);
UPDATE Order_t
SET Totalfixed = (
SELECT orderid, SUM(price * quantity) AS tf
FROM
orderline ol,
product p
WHERE
ol.productid = p.productid
AND ol.orderid = orderid
GROUP BY orderid
);
Everything works fine separately but I get:
operand should contain 1 column
And if I remove orderid from the subquery, I get:
subquery returns more than 1 row
Is there anyway to make this work without a join?
Regardless of the database, the context requires a scalar subquery. This means avoid the group by and return only one column:
UPDATE Order_t
SET Totalfixed = (
SELECT SUM(price * quantity) AS tf
FROM orderline ol JOIN
product p
ON ol.productid = p.productid
WHERE ol.orderid = Order_t.orderid
);
I also fixed the JOIN syntax (always use explicit joins) and the correlation clause so it refers to the outer query.
UPDATE A
SET Totalfixed = SUM(price * quantity)
FROM Order_t A
INNER JOIN orderline ol ON ol.orderid = A.orderid
INNER JOIN product p ON ol.productid = p.productid

SQL Query on SQL Server 2008

I'm trying to get only customers that ordered both a "Gas Range" and a "Washer". I'm getting Customers who ordered a "Gas Range" and not a "Washer" and customers with both. I need the customer that meets both conditions. I'm close but a little stuck. Below is the query that I have so far. Please let me know if you need more information.
My Tables - CUSTOMER(CUST_NUM, CUST_NAME), ORDER_LINE(ORDER_NUM, PART_NUM), ORDERS(ORDER_NUM, CUST_NUM), PART(PART_NUM, PART_DESCRIPTION)
SELECT C.CUST_NAME AS [Customer(s) that ordered a Gas Range and Washer]
FROM CUSTOMER C
INNER JOIN ORDERS O
ON C.CUST_NUM = O.CUST_NUM
INNER JOIN ORDER_LINE OL
ON O.ORDER_NUM = OL.ORDER_NUM
INNER JOIN PART P
ON OL.PART_NUM = P.PART_NUM
WHERE P.PART_DESCRIPTION IN ('GasRange','Washer')
GROUP BY C.CUST_NAME
try the following
SELECT C.CUST_NAME AS [Customer(s) that ordered a Gas Range and Washer]
FROM CUSTOMER C
INNER JOIN ORDERS O
ON C.CUST_NUM = O.CUST_NUM
INNER JOIN ORDER_LINE OL
ON O.ORDER_NUM = OL.ORDER_NUM
INNER JOIN PART P
ON OL.PART_NUM = P.PART_NUM
INNER JOIN ORDERS O2
ON C.CUST_NUM = O2.CUST_NUM
INNER JOIN ORDER_LINE OL2
ON O2.ORDER_NUM = OL2.ORDER_NUM
INNER JOIN PART P2
ON OL2.PART_NUM = P2.PART_NUM
WHERE P.PART_DESCRIPTION IN ('GasRange') and P2.PART_DESCRIPTION IN ('Washer')
GROUP BY C.CUST_NAME
EDIT: Had a further look and I'm afraid that this can't be simplified in any other way than using WITH and complicated aggregate functions, which I would say would be more complicated than this - I think the other solution suggested using WITH won't work - it joins incorrectly. You definitely can't remove order line, and you have to use the order twice as well - if it was used once, it will cover only when the customer ordered it within one order, which is not what you wanted ;)
Try this...
So basically you need to join your Parts table again to ensure the same customer ordered a "Gas Range" and a "Washer". An IN, like in your current query functions as an OR therefore you are not getting the expected result.
WITH CTE AS (
SELECT DISTINCT O.CUST_NUM FROM ORDERS O
INNER JOIN ORDER_LINE OL
ON O.ORDER_NUM = OL.ORDER_NUM
INNER JOIN PART P
ON OL.PART_NUM = P.PART_NUM
INNER JOIN PART P2
ON OL.PART_NUM = P2.PART_NUM
WHERE P.PART_DESCRIPTION IN ('GasRange')
AND P2.PART_DESCRIPTION IN ('Washer')
)
SELECT C.CUST_NAME AS [Customer(s) that ordered a Gas Range and Washer]
FROM CUSTOMER C
INNER JOIN CTE O
ON C.CUST_NUM = O.CUST_NUM

Conditionally joining tables

I want to create conditional join to a table in my T-SQL query. Tables used in this example are from Northwind database (with only one additional table ProductCategories)
Table Products and table Categories have many-to-many relationship, hence table ProductCategories comes into picture.
I need the sum of Quantity column on table OrderDetails for each of the products falling under certain category. So I have a query like one below
Select p.ProductName, Sum(od.Quantity) As Qty
From Products p
Join OrderDetails od On od.ProductID = p.ProductID
Join ProductCategories pc On pc.ProductID = p.ProductID
And pc.CategoryID = #CategoryID
Group By p.ProductName
#CategoryID is an optional parameter. So in case it's not supplied, the join to table ProductCategories will not be required
and query should look like one below
Select p.ProductName, Sum(od.Quantity) As Qty
From Products p
Join OrderDetails od On od.ProductID = p.ProductID
Group By p.ProductName
I want to achieve this without repeating the whole query with If conditions (as below)
If #CategoryID Is Null
Select p.ProductName, Sum(od.Quantity) As Qty
From Products p
Join OrderDetails od On od.ProductID = p.ProductID
Group By p.ProductName
Else
Select p.ProductName, Sum(od.Quantity) As Qty
From Products p
Join OrderDetails od On od.ProductID = p.ProductID
Join ProductCategories pc On pc.ProductID = p.ProductID
And pc.CategoryID = #CategoryID
Group By p.ProductName
This is simplified version of the query which has many other tables and conditions like ProductCategories. And will require multiple multiple If conditions and repetition of the query. I have also tried dynamically generating the query. It works but query is not readable at all.
Any solutions?
Thank you.
Try this, if you'll use properly parametrized query - there will be no performance impact but may be gain:
Select p.ProductName, Sum(od.Quantity) As Qty
From Products p
Join OrderDetails od On od.ProductID = p.ProductID
WHERE #CategoryID IS NULL OR EXISTS (SELECT * FROM ProductCategories WHERE CategoryID = #CategoryID AND ProductID = p.ProductID)
Group By p.ProductName
Actually
in your query if there can be multiple rows in ProductCategories for one row in OrderDetails - then you get duplicates of od.Quantity in your SUM - is it an intended behavior?
I believe you can left join the tables vs the implicit inner join you are doing.
In an inner join it matches the key on the source table with each instance of the key on the destination table.
Each instance of a match on the destination table generates a set of rows displaying the
match types.
With an outer join it will display the source row EVEN IF there is no matching row in the other table. If there is it will do essentially the same as an inner join and you'll get a row back for each instance of match. So you're getting the data you need when it's available and not getting what data is unavailable.
Take this as an example
select * from Products
Left join ProductAndCategory on ProductAndCategory.ProductID = Products.ProductID
left join Categories on Categories.CategoryID = ProductAndCategory.CategoryID
Where I have a simple Product table with a ProductID and a ProductName a ProductAndCategory table with a ProductID and a CategoryID and a Categories table with a CategoryID and a CategoryName
It will show the rows that have categories with the joined categories and the rows that don't have categories will just show the one row with null for the values that don't exist.
I'm not very familiar with T-SQL so I don't know if this will cut it but in mysql you could do something like
Select p.ProductName, Sum(od.Quantity) As Qty
From Products p
Join OrderDetails od On od.ProductID = p.ProductID
Join ProductCategories pc On pc.ProductID = p.ProductID
And pc.CategoryID = if(#CategoryID is null , pc.CategoryID, #CategoryId)
Group By p.ProductName
Yes and if() still remains but just "one query", if that's what you're looking for.
At the same time, you say you were able to dynamically generate a single query. If so is the readability that much of an issue? If your generated query was more performant over what I suggested above I'd go with that. It's generated; you won' be manually tweaking/reading the result.