UPDATE FROM is not working with multiple update of same row - sql

I have #Products and #TempProducts tables. I want to Update #Products from #TempProducts.
My code:
DECLARE #Products TABLE(
Id INT,
Name NVARCHAR(255),
Description NVARCHAR(255));
DECLARE #TempProducts TABLE(
RowNumber int,
Id INT,
Name NVARCHAR(255),
Description NVARCHAR(255));
INSERT INTO #Products(Id,Name,Description) VALUES(1,'Name1','Desc1');
INSERT INTO #TempProducts(RowNumber,Id,Name,Description)
VALUES(1,1,'NewName1',NULL);
INSERT INTO #TempProducts(RowNumber,Id,Name,Description)
VALUES(2,1,NULL,'NewDesc1');
INSERT INTO #TempProducts(RowNumber,Id,Name,Description)
VALUES(3,1,NULL,'NewDesc2');
INSERT INTO #TempProducts(RowNumber,Id,Name,Description)
VALUES(4,1,'NewName2',NULL);
WITH TP AS
(
SELECT TOP (100000) RowNumber,Id,Name,DESCRIPTION
FROM #TempProducts
ORDER BY RowNumber ASC
)
UPDATE P
SET Name = ISNULL(TP.Name,P.Name),
Description = ISNULL(TP.Description,P.Description)
FROM #Products P
INNER JOIN TP
ON P.Id = TP.Id
SELECT * FROM #Products
Expected:
Id Name Description
---------------------------
1 NewName2 NewDesc2
But getting:
Id Name Description
---------------------------
1 NewName1 NewDesc1
Note that I am ordering by ORDER BY RowNumber means Update RowNumber1 first then second and so on.

If there are multiple matches, then SQL Server does not specify which gets updated. If you want the last (or first) value updated, then use window functions to find it:
WITH TP AS (
SELECT t.*,
ROW_NUMBER() OVER (PARRTITION BY ID ORDER BY RowNumber DESC) as seqnum
FROM (SELECT TOP (100000) tp.*
FROM #TempProducts ORDER BY RowNumber ASC
) t
)
UPDATE P
SET Name = ISNULL(TP.Name,P.Name),
Description = ISNULL(TP.Description,P.Description)
FROM #Products P INNER JOIN
TP
ON P.Id = TP.Id AND seqnum = 1;

Use caution when specifying the FROM clause to provide the criteria
for the update operation. The results of an UPDATE statement are
undefined if the statement includes a FROM clause that is not
specified in such a way that only one value is available for each
column occurrence that is updated, that is if the UPDATE statement is
not deterministic.
I would do it like this:
UPDATE P SET Name = ISNULL(ca1.Name, P.Name), Description = ISNULL(ca2.Description, P.Description)
FROM #Products P
OUTER APPLY(SELECT TOP 1 Name FROM #TempProducts WHERE ID = p.ID AND Name IS NOT NULL ORDER BY RowNumber DESC) ca1
OUTER APPLY(SELECT TOP 1 Description FROM #TempProducts WHERE ID = p.ID AND Description IS NOT NULL ORDER BY RowNumber DESC) ca2

Related

Order by 5 consecutive rows alternating between results

I'm trying to select from two categories, but I want to alternate between categories by grouping five of each categories.
Here are the tables
SELECT MC.main_category_id, MC.main_category_name, MC.order_by_num AS MC_Order,
C.category_id, C.category_name, C.order_by_num AS C_Order, C.gift, I.item_id, I.model_num, I.item_title, B.brand_name, I.price, I.suggested_price, i.description
FROM dbo.tblMainCategories AS MC
INNER JOIN dbo.tblCategories AS C ON MC.main_category_id = C.main_category_id
INNER JOIN dbo.tblItemCategories AS IC ON MC.main_category_id = IC.main_category_id
AND C.category_id = IC.category_id
INNER JOIN dbo.tblItems AS I ON IC.ic_item_id = I.item_id
INNER JOIN dbo.tblBrands AS B ON I.brand_id = B.brand_id
WHERE (MC.store_id = 14) AND (IC.store_id = 14) AND I.isActive = 1
And MC.main_category_id in (1,2)
ORDER BY MC.order_by_num, C.order_by_num, I.order_by_num,I.price
How can I sort this result that it should be ordered by 5 of MainCategory 1 then 5 of MainCategory 2 and then 5 of MainCategory 1 and continue 5 of each until the end.
MainCategory 1 has much more items than MainCategory 2. the rest of the MainCategory 1 should also be at the bottom.
You could try something like this. Just be sure your results are order the way you want when inserting them into the SQL tables. Break the query into 2 duplicates that insert the results into some temp tables. Then you can iterate through the results inserting each 5 into a third results table.
Here is an example of the approach I took with a db I had available to me.
create table #result(AccountID int, AccountTypeID int, AccountName varchar(128))
select AccountID, ab.AccountTypeID, AccountName INTO #Temp from AccountBase ab
join AccountType at on ab.AccountTypeId = at.AccountTypeId
where ab.AccountTypeId in (1)
order by AccountName
select AccountID, ab.AccountTypeID, AccountName INTO #Temp2 from AccountBase ab
join AccountType at on ab.AccountTypeId = at.AccountTypeId
where ab.AccountTypeId in (2)
order by AccountName
While ((select count(*) from #Temp) > 0 or (select count(*) from #Temp2) > 0)
begin
If (select count(*) from #Temp) > 0
Begin
insert into #result select Top(5) * from #Temp
delete top(5) from #Temp
End
If (select count(*) from #Temp2) > 0
Begin
insert into #result select Top(5) * from #Temp2
delete top(5) from #Temp2
End
End
select * from #result
drop table #Temp
drop table #Temp2
drop table #result
I believe everything is supported by SQL 2000.

fill a Local #temptable in SQL with multiple Table variables

I'm in SQL Server Management Studio 2008 and I would like to Set Multiple OrderIDs to run them through this query. I heard that I would have to use a TempTable but I'm stuck at how to set Multiple Table variables.
This is the original query I use:
DECLARE #OrderID int
DECLARE #Status int
-- FF nummer
SET #OrderID = 134
-- Huidige status
SET #Status = 4
BEGIN TRANSACTION
UPDATE FF_Task
SET tsk_FK_CurrentStatus = NULL
WHERE (tsk_FK_Order = #OrderID)
UPDATE FF_Order
SET ord_FK_CurrentOrderStatus = NULL
WHERE (ord_PK = #OrderID)
DELETE FROM FF_StatusLog
WHERE (stl_PK IN
(SELECT TOP (1) FF_StatusLog_1.stl_PK
FROM FF_StatusLog AS FF_StatusLog_1 INNER JOIN
FF_Task ON FF_StatusLog_1.stl_FK_Task = FF_Task.tsk_PK
WHERE (FF_Task.tsk_FK_Order = #OrderID)
ORDER BY FF_StatusLog_1.stl_PK DESC))
DELETE
FROM FF_OrderStatusLog
WHERE (osl_Status = #Status) AND (osl_FK_Order = #OrderID)
UPDATE FF_Task
SET
tsk_FK_CurrentStatus = (SELECT TOP (1) FF_StatusLog_1.stl_PK
FROM FF_StatusLog AS FF_StatusLog_1 INNER JOIN
FF_Task ON FF_StatusLog_1.stl_FK_Task = FF_Task.tsk_PK
WHERE (FF_Task.tsk_FK_Order = #OrderID)
ORDER BY FF_StatusLog_1.stl_PK DESC)
WHERE (tsk_FK_Order = #OrderID)
UPDATE FF_Order
SET ord_FK_CurrentOrderStatus = (SELECT TOP 1 osl_Status FROM
FF_OrderStatusLog WHERE (osl_FK_Order = #OrderID) ORDER BY osl_CreationDateTime DESC)
WHERE (ord_PK = #OrderID)
COMMIT TRANSACTION
Thank you.
You can use table variable or temp table
Also in SQLServer2008+ you can passing table-valued parameters to SPs and UDFs.A table-value parameter allows you to pass rows of data to your SPs and UDFs in tabular format. To create a table-valued parameter you must first create a table type that defines your table structure.
DECLARE #TableOfParemeters TABLE(OrderID int, Status int)
INSERT #TableOfParemeters
VALUES(134, 4),
(135, 5)
BEGIN TRANSACTION
UPDATE FF_Task
SET tsk_FK_CurrentStatus = NULL
WHERE tsk_FK_Order IN (SELECT OrderID FROM #TableOfParemeters)
UPDATE FF_Order
SET ord_FK_CurrentOrderStatus = NULL
WHERE ord_PK IN (SELECT OrderID FROM #TableOfParemeters)
;WITH cte AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY stl_PK ORDER BY stl_PK DESC) AS rn
FROM FF_StatusLog stl INNER JOIN FF_Task ff ON stl.stl_FK_Task = ff.tsk_PK
WHERE ff.tsk_FK_Order IN (SELECT OrderID FROM #TableOfParemeters)
)
DELETE cte
WHERE rn = 1
DELETE
FROM FF_OrderStatusLog
WHERE EXISTS (
SELECT 1
FROM #TableOfParemeters t
WHERE osl_Status = t.Status AND osl_FK_Order = t.OrderID
)
;WITH cte AS
(
SELECT stl.stl_PK, ff.tsk_FK_CurrentStatus,
ROW_NUMBER() OVER(PARTITION BY stl_PK ORDER BY stl_PK DESC) AS rn
FROM FF_StatusLog stl INNER JOIN FF_Task ff ON stl.stl_FK_Task = ff.tsk_PK
WHERE ff.tsk_FK_Order IN (SELECT OrderID FROM #TableOfParemeters)
)
UPDATE cte
SET tsk_FK_CurrentStatus = stl_PK
WHERE rn = 1
;WITH cte AS
(
SELECT o.ord_FK_CurrentOrderStatus, l.osl_Status,
ROW_NUMBER() OVER(PARTITION BY osl_Status ORDER BY osl_CreationDateTime DESC) AS rn
FROM FF_Order o INNER JOIN FF_OrderStatusLog l ON o.ord_PK = l.osl_FK_Order
WHERE l.osl_FK_Order IN (SELECT OrderID FROM #TableOfParemeters)
)
UPDATE cte
SET tsk_FK_CurrentStatus = osl_Status
WHERE rn = 1
COMMIT TRANSACTION

Update with CTE and row-numbers as sequence or TSQL update query with ROW_NUMBER()

I have a table named Site with columns Name, SiteId and Sequence. I would like to fill the Sequence field with the rownumber. I've tried the following query, but it just doesn't update the records:
WITH RowNumbers AS
(
select SiteId,
RowNum = row_number() OVER ( order by SiteId )
from [Site]
)
UPDATE s
SET s.[Sequence] = r.RowNum
FROM [Site] as s INNER JOIN RowNumbers as r ON s.SiteId = r.Row
What am I doing wrong?
You can update the CTE directly...
WITH RowNumbers AS
(
select *,
RowNum = row_number() OVER ( order by SiteId )
from [Site]
)
UPDATE RowNumbers
SET [Sequence] = RowNum
This works in the same way as an updatable view. I added * to ensure the the updated field comes through, and then updated it directly.
You should join to r.SiteID, not r.Row
WITH RowNumbers AS
(
select SiteId,
RowNum = row_number() OVER ( order by SiteId )
from [Site]
)
UPDATE s
SET s.[Sequence] = r.RowNum
FROM [Site] as s INNER JOIN RowNumbers as r ON s.SiteId = r.SiteID

Distinct SQL when many to many

3 tables sto_Product, sto_ProductXCategory and sto_Category
sto_Product has all the product info (bvin=Primary Key)
sto_Category has all the Categoryinfo (bvin=Primary Key)
sto_ProductXCategory (ProductId and CategoryId are PK and FK)
currently it results in duplicate products because some products are duplicate in sto_ProductXCategory (with different CategoryId's).
this SQL statement is part of a larger stored procedure so I'm trying to keep it similar.
This is the statement I'm trying to fix
DECLARE #Sortorder int =6;
with product as
(
select distinct p.*
from sto_Product p
inner join sto_ProductXCategory px on(p.bvin = px.productid)
where p.parentID = ''
and p.Status = 1
and p.siteprice >=0
and p.siteprice <= 2147483647
and (
(p.manufacturerid = '8036ab2c-641e-487b-a577-76c09c5c3cc9'
)
)
),
AllProducts AS
(
SELECT
RowNum =
CASE
WHEN #Sortorder = 0
THEN ROW_NUMBER() OVER (ORDER BY px.SortOrder)
WHEN #Sortorder = 1
THEN ROW_NUMBER() OVER (ORDER BY px.SortOrder)
WHEN #Sortorder = 2
THEN ROW_NUMBER() OVER (ORDER BY ProductName)
WHEN #Sortorder = 3
THEN ROW_NUMBER() OVER (ORDER BY SitePrice)
WHEN #Sortorder = 4
THEN ROW_NUMBER() OVER (ORDER BY SitePrice DESC)
WHEN #Sortorder = 6
THEN ROW_NUMBER() OVER (ORDER BY ProductName DESC)
ELSE ROW_NUMBER() OVER (ORDER BY px.SortOrder)
END,
p.*
from product p
inner join sto_ProductXCategory px on(p.bvin = px.productid)
)
SELECT AllProducts.RowNum,*,
(SELECT COUNT(*) FROM AllProducts) As TotalRowCount
FROM AllProducts
WHERE RowNum BETWEEN (1) and (12 )
ORDER BY AllProducts.RowNum
Sample Dataset
sto_Product
bvin,sku,ProductName,SitePrice
0001,001,AProductName,100
0002,002,BProductName,50
0003,003,CProductName,75
sto_Category
bvin,CategoryName
1000,ACategoryName
2000,BCategoryName
3000,CCategoryName
sto_ProductXCategory
ProductId,CategoryId,SortOrder
0001,1000,1
0001,2000,2
0001,3000,3
0002,2000,4
0003,2000,5
0003,3000,6
thanks in advance
You've stated that
...currently it results in duplicate products because some products are duplicate in sto_ProductXCategory (with different CategoryId's).
But you havent said what it is you want to happen.
For my purposes here, I'll assume you want only one row per product, ignoring the different values of CategoryID appearing in sto_ProductXCategory
In AllProducts you have:
from product p inner join sto_ProductXCategory px on(p.bvin = px.productid)
this needs to be changed to filter the duplicate rows in sto_ProductXCategory that you do not want in you output dataset;
from product p join (
select productid, min(categoryid) as Category
from sto_ProductXCategory
group by productid
) x on p.bvin = x.productid
join sto_ProductXCategory px on p.bvin = px.Productid and x.Category = px.CategoryID
How's that?

sql comma delimited list of a column in analytical function

I am using SQL server 2008, and I need to make a common delimeted list of a column. I know how to do it, but I need this time while I use analytical function, I mean I don't want to use group by clause.
Since I will also select the records in outer query "where row_num=1"
Here is the query:
SELECT UserId
,ProductList
,Value
FROM
(
SELECT p.UserId
,p.Value
, ROW_NUMBER()OVER (PARTITION BY p.UserId ORDER BY p.RecordCreateDate asc) AS 'row_num'
--here I need a solution OVER (PARTITION BY p.UserId) AS 'ProductList'
FROM Products p
INNER JOIN
Users u
ON p.UserId = u.Id
) result
WHERE result.row_num = 1
Users data:
Id Name ....
1 John
2 Anton
3 Craig
Products data:
Id UserId Name RecordCreateDate Value
1 1 a 21.12.2012 10
2 1 b 11.12.2012 20
3 1 c 01.12.2012 30
4 2 e 05.12.2012 40
5 2 f 17.12.2012 50
6 3 d 21.12.2012 60
7 3 i 31.12.2012 70
I need a result such as:
UserId ProductList Value
1 a,b,c 30
2 e,f 40
3 d,i 60
Thanks for your help
Since you are going to filter on row_num = 1 you need to put your query in a CTE or the likes where you include the extra columns from Products. Then you can build your comma separated string in the outer query using the for XML path trick without using group by.
;WITH C as
(
SELECT p.UserId
, ROW_NUMBER()OVER (PARTITION BY p.UserId ORDER BY p.RecordCreateDate asc) AS 'row_num'
--, Some other fields from Products
FROM Products p
INNER JOIN
Users u
ON p.UserId = u.Id
)
SELECT UserId,
--, Some other fields from Products
--, Build the concatenated list here using for xml path()
FROM C
WHERE C.row_num = 1
Just for completeness. Remove the # symbols for your actual solution.
SET NOCOUNT ON;
CREATE TABLE #users
(
Id INT,
Name VARCHAR(32)
);
INSERT #users VALUES
(1,'John'),
(2,'Anton'),
(3,'Craig');
CREATE TABLE #products
(
Id INT,
UserId INT,
Name VARCHAR(32),
RecordCreateDate DATE,
Value INT
);
INSERT #products VALUES
(1,1,'a','2012-12-21',10),
(2,1,'b','2012-12-11',20),
(3,1,'c','2012-12-01',30),
(4,2,'e','2012-12-05',40),
(5,2,'f','2012-12-17',50),
(6,3,'d','2012-12-21',60),
(7,3,'i','2012-12-31',70);
The query:
;WITH x AS
(
SELECT UserId, Value,
row_num = ROW_NUMBER() OVER
(
PARTITION BY UserId
ORDER BY RecordCreateDate
)
FROM #products
)
SELECT
x.UserId,
u.Name,
ProductList = STUFF((
SELECT ',' + Name
FROM #Products AS p
WHERE p.UserId = x.UserId
FOR XML PATH(''),
TYPE).value(N'./text()[1]', N'varchar(max)'),1,1,''),
x.Value
FROM x
INNER JOIN #users AS u
ON x.UserId = u.Id
WHERE x.row_num = 1;
Then clean up:
DROP TABLE #users, #products;
Results:
UserId Name ProductList Value
1 John a,b,c 30
2 Anton e,f 40
3 Craig d,i 60
I'm not sure what you're asking in the beginning, but this will give you the requested output
SELECT UserId,
STUFF((SELECT ',' + ProductName from Products p WHERE p.UserID = u.UserID FOR XML PATH('')), 1, 1, '') as ProductList
FROM Users u