I'm using HP Vertica 7 as data warehouse DB.
I need to update two tables at the same time.
foreach (row r in table_1)
{
foreach (row r2 in table_2)
{
if(r.key1 == r2.key1 && r.key2 ==r2.key2 && r.soldQuantity > r2.Quantity)
{
updateRowInT1(); //r.Partner = r2.Partner;
updateRowInT2(); //r2.Quantity = r2.Quantity - r.soldQuantity;
break;
}
}
}
This part of code best describes what I need to do.
Is there any way to do this in SQL (with user-defined functions).
Because of changes in second table I can't use update and join. Or I can?
Also, this is part of ETL process done by Pentaho Kettle. Is perhaps way to do this inside this tool.?
Thanks
Edit:
I have changed code above.
What I need to achieve?
Regular Sales by Partner.
What's wrong right now?
When some SKU is sold in retail shop, I can see SKU LAST Partner. But, company I work for is buying same SKU from more Partners.
Slowly Changing Dimension is not applicable because when we switch partner on SKU, there is still some quantity of that SKU on shelf.
Assumption:
FIFO - first In first Out.
So, for initial matching, I need to compare (in date order) both sales and buying and to decrement bought quantity.
You probably should be thinking in a more set oriented way.
Assuming I understand what you are trying to do properly, I would do this in two update statements:
-- Update table_1's Partner based on a row in table_2.
UPDATE table_1 r
SET Partner = r2.Partner
FROM table_2 r2
WHERE r2.key1 = r.key1
AND r2.key2 = r.key2;
-- Reduce the Quantity in table_2 based on table_1's soldQuantity
UPDATE table_2 r2
SET Quantity = r2.Quantity - r.soldQuantity
FROM table_1 r
WHERE r.key1 = r2.key1
AND r.key2 = r2.key2
AND r2.Quantity >= r.soldQuantity;
-- Both of these should be done in a transaction so it is all or none. Commit the work.
COMMIT;
You can see the SQLFiddle here.
Related
Background
I am currently working on a simple access database for a small bookstore. The business sells books as well as other items.
See following ER-diagram here
Problem
My problem starts in the table Transaction. I would like to create a simple form where a subtotal is calculated as quantity purchased multiplied by the price (both of a certain item).
However the Price field is either in the BookInfo, FoodAndBev, or ApparelAndSupplies table.
Question
How can I traverse the Transaction table to Items and to one of the tables I listed depending on the StockNum in Transaction?
For example, a customer buys an item with StockNum BK001. I would like to retrieve the Price corresponding to BK001 in the BookInfo table and return to Transaction to be used for calculating the subtotal.
The field StockNum exists in tables BookInfo, FoodAndBev, and ApparelAndSupplies.
Sample Data: BookInfo, Items, Transaction
Ideal Output: Transaction 1: A customer buys 2 copies of a book with StockNum 'BK001' (disregard what I already have in the screenshot). The Price of 'BK001' is $119.99 which I would need to retrieve from BookInfo (my problem is I want to make sure it goes to BookInfo not the other two tables which have StockNum also), so the subtotal would then be calculated and would show $239.98. For now, I've been doing this manually. I want to be able to do this with different StockNum such as those that start with AS for Apparel and Supplies and FB for food and beverage.
Thanks in advance!
Left-Joining all tables and using Case-When-Expression (which is function switch in Access) for different price multiplication:
SELECT t.StockNum, t.NumOrdered,
switch(
Left(t.StockNum,2) = 'BK' , (t.NumOrdered * b.Price),
Left(t.StockNum,2) = 'AS' , (t.NumOrdered * a.Price),
Left(t.StockNum,2) = 'FB' , (t.NumOrdered * f.Price),
) AS subtotal
FROM Transaction t
LEFT JOIN BookInfo b ON b.StockNum = t.SockNum
LEFT JOIN ApparelAndSupplies a ON a.StockNum = t.SockNum
LEFT JOIN FoodAndBev f ON f.StockNum = t.SockNum
Ok I have a rather unique situation and I can't believe there is not a better way of doing this than my solution.
Requirements:
Table 2 - EpmTask_UserView_RM is a subset of table 1 -
MSP_EpmTask_UserView So while all the fields match Table 1 has many
more rows than table 2
Table 2 needs to get updated from table 1 based on the date a task has changed (We can't do a drop and replace) There are three cases:
Task updates where something has changed about the task (We will know based on the task date stamp)
Task Deletes where a task has been deleted
Task Adds where a new task exists
I have 3 different queries that do this and am thinking there is a better way.
**** DELETE Tasks from ZZZ_TEST_OF_UPDATE_MSP_EpmTask_UserView_RM table if no longer present in Production***/
USE [ProjectWebApp]
GO
DELETE FROM [dbo].[ZZZ_TEST_OF_UPDATE_MSP_EpmTask_UserView_RM]
WHERE [dbo].[ZZZ_TEST_OF_UPDATE_MSP_EpmTask_UserView_RM].TaskUID IN
(SELECT
/*Subquery to select all records in ZZZ_TEST_OF_UPDATE_MSP_EpmTask_UserView_RM NOT found in MSP_EpmTask_UserView_RM */
[ProjectWebApp].[dbo].[ZZZ_TEST_OF_UPDATE_MSP_EpmTask_UserView_RM].[TaskUID]
FROM [ProjectWebApp].[dbo].[ZZZ_TEST_OF_UPDATE_MSP_EpmTask_UserView_RM]
LEFT JOIN [MSPSPRO].[ProjectWebApp].[dbo].[MSP_EpmTask_UserView] as Prod
on Prod.TaskUID = [ProjectWebApp].[dbo].[ZZZ_TEST_OF_UPDATE_MSP_EpmTask_UserView_RM].TASKuid
where Prod.TaskUID is NULL)
Query 2 the Update
UPDATE [dbo].[ZZZ_TEST_OF_UPDATE_MSP_EpmTask_UserView_RM]
SET
[ProjectUID] = Source.[ProjectUID]
,[TaskUID] = Source.[TaskUID]
,[TaskName] = Source.[TaskName]
,[TaskIndex] = Source.[TaskIndex]
,[TaskOutlineLevel] = Source.[TaskOutlineLevel]
,[TaskOutlineNumber] = Source.[TaskOutlineNumber]
,[TaskStartDate] = Source.[TaskStartDate]
,[TaskFinishDate] = Source.[TaskFinishDate]
,[TaskActualStartDate] = Source.[TaskActualStartDate]
,[TaskActualFinishDate] = Source.[TaskActualFinishDate]
,[TaskPercentCompleted] = Source.[TaskPercentCompleted]
,[Health] = Source.[Health]
,[Milestone Significance Level] = Source.[Milestone Significance Level]
,[TaskModifiedDate] = Source.[TaskModifiedDate]
,[TaskBaseline1StartDate] = Source.[TaskBaseline1StartDate]
,[TaskBaseline1FinishDate] = Source.[TaskBaseline1FinishDate]
,[TaskBaseline1Duration] = Source.[TaskBaseline1Duration]
,[QueryTimestamp] = GetDate()
FROM [MSPSPRO].[ProjectWebApp].[dbo].[MSP_EpmTask_UserView] AS Source
WHERE Source.TaskUID = [dbo].[ZZZ_TEST_OF_UPDATE_MSP_EpmTask_UserView_RM].TaskUID
AND GetDate() - Source.TaskModifiedDate <= .01 -- Update any task changed in last 14 minutes (14 minutes = 1% of a full day, ie '.01')
GO
Task 3 the add
SELECT
[MSP_EpmProject_UserView].[ProjectUID]
,[TaskUID]
,[TaskName]
,[TaskIndex]
,[TaskOutlineLevel]
,[TaskOutlineNumber]
,[TaskStartDate]
,[TaskFinishDate]
,[TaskActualStartDate]
,[TaskActualFinishDate]
,[TaskPercentCompleted]
,[Health]
,[Milestone Significance Level]
,[TaskModifiedDate]
,[TaskBaseline1StartDate]
,[TaskBaseline1FinishDate]
,[TaskBaseline1Duration]
,GetDate() as QueryTimestamp
INTO [ProjectWebApp].[dbo].[MSP_EpmTask_UserView_RM]
FROM [MSPSPRO].[ProjectWebApp].[dbo].[MSP_EpmTask_UserView]
Inner Join [MSPSPRO].[ProjectWebApp].[dbo].[MSP_EpmProject_UserView]
on [MSP_EpmProject_UserView].projectUID = [MSP_EpmTask_UserView].ProjectUID
WHERE [SMO Programs] = 'SMO Day 1 Release Management'
AND [Milestone Significance Level] is not null
/*AND [TaskModifiedDate] > (getdate() - 1)*/
Thoughts?
This looks like an ideal situation for a MERGE statement. If you haven't used them much or at all, I'd strongly suggest this site as a primer.
A MERGE can carry out INSERT, UPDATE, and DELETE in one shot, in the right conditions. The basic idea is that you compare rows in two tables, your source and destination, and from that comparison (and potentially other conditions) you then take the appropriate action.
MERGE can perform very well because it carries out these actions in bulk - but do test it out. Sometimes people have found them to be slower than using the separate statements in some situations. Indexing correctly (Microsoft suggest indexing the columns used to join in both tables) can help immensely. Writing the MERGE statement correctly and well is important in terms of both getting the right result, and getting good performance - so definitely do your reading up if you haven't used them before. The above link is a good starter, but there are plenty of other articles around.
INSERT INTO Shipments (Column1...Column200)
SELECT
O.Value1,...
CL.Value199,
isnull(P.PriceFactor1,1)
FROM Orders O
JOIN Clients CL on O.ClientNo = CL.ClientNo
JOIN Calc C on CL.CalcCode = C.CalcCode
JOIN Prices P on CL.PriceKey = P.PriceKey
WHERE O.PriceFactor1 = P.PriceFactor1
AND O.PriceFactor2 = P.PriceFactor2
AND O.PriceFactor3 = P.PriceFactor3
The above query (part of a new stored procedure meant to replace an old and nasty one that used a cursor...) fails to return some rows, because the rows in Orders do not have matching rows in Prices. In such cases, we want the last value in the INSERT list to be 1 by default. Instead, the row is never built; or, when we tried to fix it by changing the WHERE conditions, it brought PriceFactor1 from a different row, which is also no good.
How it's supposed to work:
A row is created in table Orders. A third-party program then executes a stored procedure (Asp_BuildShipments) and displays the results once they have been inserted into table Shipments. This SP is meant to populate the table Shipments by pulling values from Orders, Clients, Drivers, Vehicles, Prices, Routes, and others. It's a long SP, and the array of tables is big and varied.
In table Orders:
PriceFactor1 | PriceFactor2 | PriceFactor3
12 | 10 | 8
In table Prices:
PriceFactor1 | PriceFactor2 | PriceFactor3
18 | 12 | 10
In a case such as this, the SP needs to recognize that no such rows exist in Prices and use a default value of 1 rather than skipping the row or pulling the price from a different row.
We've tried isnull(), CASE statements, and WHERE EXISTS, but to no avail.
The new SP is set based, and we want to leave it that way - the old one took minutes, the new one takes only a few seconds. But without passing row by row, we aren't sure how to check each individual Order to see if has a matching Price before building the row in Shipments.
I know there are details missing here, but I didn't want to write a 1,000 page question. If these details are insufficient, I'll post as much as I need to to help get your brains storming. Been stuck on this for a while now...
Thanks in advance :)
Could this be as simple as:
SELECT
O.Value1,...
CL.Value199,
isnull(P.PriceFactor1,1)
FROM Orders O
JOIN Clients CL on O.ClientNo = CL.ClientNo
JOIN Calc C on CL.CalcCode = C.CalcCode
LEFT JOIN Prices P on CL.PriceKey = P.PriceKey
AND O.PriceFactor1 = P.PriceFactor1
AND O.PriceFactor2 = P.PriceFactor2
AND O.PriceFactor3 = P.PriceFactor3
Richard Hansell's answer should work if the problem is as you suggest there are no matching rows in Prices. Since it's not working the problem is in the other joins.
One of these joins filters out data.
JOIN Clients CL on O.ClientNo = CL.ClientNo
JOIN Calc C on CL.CalcCode = C.CalcCode
I have an Access Database which links into Excel for a college database project. The goal is to fix incorrect/incomplete data before importation through the use of Update/SQL queries. There is one item in particular which is a field containing the order status. If an order is complete = Complete, if an order is missing a few parts = Backorder and nothing at all = Open. The issue I have is there is multiple parts on one PO ID# which makes it difficult to determine if an order is on backorder as 2/5 parts may be complete while 3/5 may be on backorder. Any ideas on how I can force access to automatically set a default order status or is this a long sql query?
Thanks
With SQL you can return a query to find the order status, using a CASE statement. I'm not sure of the table/column names in the parent table you have but you can probably tweak it from here (I'm just assuming identity columns):
SELECT P.Id, P.Name, "Status" =
CASE
WHEN R.QuantityReceived < I.QuantityOrdered THEN 'Backorder'
WHEN R.QuantityReceived = I.QuantityOrdered THEN 'Complete'
WHEN R.QuantityReceived > I.QuantityOrdered THEN 'Open'
END
FROM PurchaseOrders P INNER JOIN POItem I on P.PurchaseOrderId = I.PurchaseOrderId
INNER JOIN POReceipt R on I.ItemId = R.ItemId
So first you need to join your tables to get the related records (this is also assuming that there is only one of each), then use a CASE statement to return the value you are looking for based on a comparison between fields.
I have a data set that lists the date and quantity of future stock of products. Occasionally our demand outstrips our future supply and we wind up with a negative future quantity. I need to factor that future negative quantity into previous supply so we don't compound the problem by overselling our supply.
In the following data set, I need to prepare for demand on 10-19 by applying the negative quantity up the chain until i'm left with a positive quantity:
"ID","SKU","DATE","SEASON","QUANTITY"
"1","001","2012-06-22","S12","1656"
"2","001","2012-07-13","F12","1986"
"3","001","2012-07-27","F12","-283"
"4","001","2012-08-17","F12","2718"
"5","001","2012-08-31","F12","-4019"
"6","001","2012-09-14","F12","7212"
"7","001","2012-09-21","F12","782"
"8","001","2012-09-28","F12","2073"
"9","001","2012-10-12","F12","1842"
"10","001","2012-10-19","F12","-12159"
I need to get it to this:
"ID","SKU","DATE","SEASON","QUANTITY"
"1","001","2012-06-22","S12","1656"
"2","001","2012-07-13","F12","152"
I have looked at using a while loop as well as an outer apply but cannot seem to find a way to do this yet. Any help would be much appreciated. This would need to work for sql server 2008 R2.
Here's another example:
"1","002","2012-07-13","S12","1980"
"2","002","2012-08-10","F12","-306"
"3","002","2012-09-07","F12","826"
Would become:
"1","002","2012-07-13","S12","1674"
"3","002","2012-09-07","F12","826"
You don't seem to get a lot of answers - so here's something if you won't get the right 'how-to do it in pure SQL'. Ignore this solution if there's anything SQLish - it's just a defensive coding, not elegant.
If you want to get a sum of all data with same season why deleting duplicate records - just get it outside, run a foreach loop, sum all data with same season value, update table with the right values and delete unnecessary entries. Here's one of the ways to do it (pseudocode):
productsArray = SELECT * FROM products
processed = array (associative)
foreach product in productsArray:
if product[season] not in processed:
processed[season] = product[quantity]
UPDATE products SET quantity = processed[season] WHERE id = product[id]
else:
processed[season] = processed[season] + product[quantity]
DELETE FROM products WHERE id = product[id]
Here is a CROSS APPLY - tested
SELECT b.ID,SKU,b.DATE,SEASON,QUANTITY
FROM (
SELECT SKU,SEASON, SUM(QUANTITY) AS QUANTITY
FROM T1
GROUP BY SKU,SEASON
) a
CROSS APPLY (
SELECT TOP 1 b.ID,b.Date FROM T1 b
WHERE a.SKU = b.SKU AND a.SEASON = b.SEASON
ORDER BY b.ID ASC
) b
ORDER BY ID ASC