Update query with order by - sql

I am trying to update one column based on another column short. I am using order by, but while using select top(10000) my incremental number going 4000 series but I need from 101, but initially I declare int 100
DECLARE #IncrementValue int
SET #IncrementValue = 100
UPDATE CabecDoc
SET CDU_NumberBook = #IncrementValue,
#IncrementValue = #IncrementValue + 1
FROM
(SELECT TOP(7000) *
FROM CabecDoc
WHERE data BETWEEN '2019-05-01' AND '2019-07-17'
AND Entidade = 'VD4'
AND tipodoc = 'VD' AND CDU_SimbolBook = '*'
ORDER BY NumDoc ASC) CabecDoc
I need update column from 101 to an incremental number through 7000 records.

Here's what you need. No need to declare variables.
UPDATE
CabecDoc
SET
CDU_NumberBook = 100 + RowNum
FROM
(
Select
top(7000) *,
ROW_NUMBER() OVER(
ORDER BY
NumDoc
) AS RowNum
FROM
CabecDoc
WHERE
data between '2019-05-01'
and '2019-07-17'
and Entidade = 'VD4'
and tipodoc = 'VD'
and CDU_SimbolBook = '*'
ORDER BY
RowNum ASC
) CabecDoc

Related

Update subsequent row data if the first line has been updated in SQL

How to update the subsequent row if the data at first row has the changes made?
How to construct a proper SQL query for this situation?
ID ACTION ORIBALANCE TRANAMOUNT NEWBALANCE
1, Deposit, 1000, 100,1100
2,Deposit,1100,300,1400
3,Deposit,1400,500,1900
4,Withdraw,1900,-300,1600
5,Withdraw,1600,-500,1100
If I update the row ID 1, I changed the tranamount from 100 to 500, so the newbalance will be updated to 1500. RowID 2 will update its oribalance from 1100 to 1500. It is cummulative.
Expected result
ID ACTION ORIBALANCE TRANAMOUNT NEWBALANCE
1, Deposit, 1000, 500,1600
2,Deposit,1600,300,1900
3,Deposit,1900,500,2400
4,Withdraw,2400,-300,2100
5,Withdraw,2100,-500,1600
If I got it correctly, you want to update every row according to the change amount.
DECLARE #ChangeAmount INT = 500
DECLARE #ID INT = 1
UPDATE DataTable SET
ORIBALANCE = ORIBALANCE + ( CASE WHEN #ID > ID THEN #ChangeAmount ELSE 0 END ),
NEWBALANCE = NEWBALANCE + #ChangeAmount
WHERE ID >= #ID
You can update it with a workflow kind of
-- Start updating row id = 2 for example
declare #id int = 2;
update t set TRANAMOUNT = 500
where id = #id;
-- and update following rows
declare #startb int = (select top(1) ORIBALANCE from t
where id = #id);
with c as
(
select id, action, ORIBALANCE, TRANAMOUNT, NEWBALANCE
, s = #startb + sum(TRANAMOUNT) over(order by id) - TRANAMOUNT
, n = #startb + sum(TRANAMOUNT) over(order by id)
from t
where id >= #id
)
update c
set ORIBALANCE = s, NEWBALANCE = n;
But I advise against that because a single row updates triggers blocking the whole table.
Consider creating a view for ORIBALANCE, NEWBALANCE data.
You seem to want a cumulative sum and adding in the first value:
select ID, ACTION, TRANAMOUNT,
(sum(TRANAMOUNT) over (order by id) -
TRANAMOUNT +
first_value(ORIBALANCE) over (order by id)
) as new_oribalance
from t;
You can easily put this into an update:
with toupdate as (
select t.*
(sum(TRANAMOUNT) over (order by id) -
TRANAMOUNT +
first_value(ORIBALANCE) over (order by id)
) as new_oribalance
from t
)
update toupdate
set oribalance = new_oribalance
where oribalance <> new_oribalance;

Selecting data from table where sum of values in a column equal to the value in another column

Sample data:
create table #temp (id int, qty int, checkvalue int)
insert into #temp values (1,1,3)
insert into #temp values (2,2,3)
insert into #temp values (3,1,3)
insert into #temp values (4,1,3)
According to data above, I would like to show exact number of lines from top to bottom where sum(qty) = checkvalue. Note that checkvalue is same for all the records all the time. Regarding the sample data above, the desired output is:
Id Qty checkValue
1 1 3
2 2 3
Because 1+2=3 and no more data is needed to show. If checkvalue was 4, we would show the third record: Id:3 Qty:1 checkValue:4 as well.
This is the code I am handling this problem. The code is working very well.
declare #checkValue int = (select top 1 checkvalue from #temp);
declare #counter int = 0, #sumValue int = 0;
while #sumValue < #checkValue
begin
set #counter = #counter + 1;
set #sumValue = #sumValue + (
select t.qty from
(
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY id ASC) AS rownumber,
id,qty,checkvalue
FROM #temp
) AS foo
WHERE rownumber = #counter
) t
)
end
declare #sql nvarchar(255) = 'select top '+cast(#counter as varchar(5))+' * from #temp'
EXECUTE sp_executesql #sql, N'#counter int', #counter = #counter;
However, I am not sure if this is the best way to deal with it and wonder if there is a better approach. There are many professionals here and I'd like to hear from them about what they think about my approach and how we can improve it. Any advice would be appreciated!
Try this:
select id, qty, checkvalue from (
select t1.*,
sum(t1.qty) over (partition by t2.id) [sum]
from #temp [t1] join #temp [t2] on t1.id <= t2.id
) a where checkvalue = [sum]
Smart self-join is all you need :)
For SQL Server 2012, and onwards, you can easily achieve this using ROWS BETWEEN in your OVER clause and the use of a CTE:
WITH Running AS(
SELECT *,
SUM(qty) OVER (ORDER BY id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS RunningQty
FROM #temp t)
SELECT id, qty, checkvalue
FROM Running
WHERE RunningQty <= checkvalue;
One basic improvement is to try & reduce the no. of iterations. You're incrementing by 1, but if you repurpose the logic behind binary searching, you'd get something close to this:
DECLARE #RoughAverage int = 1 -- Some arbitrary value. The closer it is to the real average, the faster things should be.
DECLARE #CheckValue int = (SELECT TOP 1 checkvalue FROM #temp)
DECLARE #Sum int = 0
WHILE 1 = 1 -- Refer to BREAK below.
BEGIN
SELECT TOP (#RoughAverage) #Sum = SUM(qty) OVER(ORDER BY id)
FROM #temp
ORDER BY id
IF #Sum = #CheckValue
BREAK -- Indicating you reached your objective.
ELSE
SET #RoughAverage = #CheckValue - #Sum -- Most likely incomplete like this.
END
For SQL 2008 you can use recursive cte. Top 1 with ties limits result with first combination. Remove it to see all combinations
with cte as (
select
*, rn = row_number() over (order by id)
from
#temp
)
, rcte as (
select
i = id, id, qty, sumV = qty, checkvalue, rn
from
cte
union all
select
a.id, b.id, b.qty, a.sumV + b.qty, a.checkvalue, b.rn
from
rcte a
join cte b on a.rn + 1 = b.rn
where
a.sumV < b.checkvalue
)
select
top 1 with ties id, qty, checkvalue
from (
select
*, needed = max(case when sumV = checkvalue then 1 else 0 end) over (partition by i)
from
rcte
) t
where
needed = 1
order by dense_rank() over (order by i)

How to Update multiple columns with using ABS,DATEDIFF

DECLARE
#deviationtime int,
#actualtime datetime,
#estimatedtime datetime
While (Select TOp 1 successrate FROM YBS_Tahmin_Kayit where deviationtime = 999 order by recordtime desc) = 999
BEGIN
SET #actualtime= (SELECT Top 1 actualtime From [TTS].[dbo].YBS_Tahmin_Kayit where deviationtime = 999 order by recordtime Desc )
SET #estimatedtime = (SELECT Top 1 estimatedtime From [TTS].[dbo].YBS_Tahmin_Kayit where deviationtime = 999 order by recordtime Desc )
SET #deviationtime = ABS(DATEDIFF(second, #actualtime, #estimatedtime))
Update [TTS].[dbo].YBS_Tahmin_Kayit SET deviationtime = #deviationtime where deviationtime = 999 and estimatedtime= #estimatedtime
end
I SETTED ALL deviationtime = 999 in database.
I want update all deviation time from database but my code making 1 by 1.

sql select into select in function

When I try to alter the function below I get the following error message:
Only one expression can be specified in the select list when the
subquery is not introduced with EXISTS.
I guess it is probably because of select into select. But why does this select into select work separately ( not in function ) but not in function.
ALTER FUNCTION [dbo].[Getcurrentexchangerate] (#CurrencyFromId INT,
#CurrencyToId INT)
returns DECIMAL(13, 10)
AS
BEGIN
DECLARE #rate DECIMAL (13, 10)
DECLARE #dw INT
SET #dw = (SELECT Datepart(dw, Getdate()))
IF( #dw != 2 ) -- Monday
BEGIN
SET #rate = (SELECT TOP (1) [rate]
FROM currencyconversionrate
WHERE [currencyfromid] = #CurrencyFromId
AND [currencytoid] = #CurrencyToId
ORDER BY id DESC)
END
ELSE
BEGIN
SET #rate = (SELECT *
FROM (SELECT TOP(2) Row_number()
OVER (
ORDER BY id DESC) AS
rownumber,
rate
FROM currencyconversionrate
WHERE ( [currencyfromid] = 2
AND [currencytoid] = 5 )
ORDER BY id DESC) AS Rate
WHERE rownumber = 2)
END
IF( #rate IS NULL )
BEGIN
SET #rate = 1
END
RETURN #rate
END
See your "else" part
SET #rate = (SELECT *
FROM (SELECT TOP(2) Row_number()
OVER (
ORDER BY id DESC) AS
rownumber,
rate
FROM currencyconversionrate
WHERE ( [currencyfromid] = 2
AND [currencytoid] = 5 )
ORDER BY id DESC) AS Rate
WHERE rownumber = 2)
You're trying to select all fields from currencyconversionrate table, you can't do that, or do you want to select "RATE" column only?
Try changing to below:
SET #rate = (SELECT rate
FROM (SELECT TOP(1) Row_number()
OVER (
ORDER BY id DESC) AS
rownumber,
rate
FROM currencyconversionrate
WHERE ( [currencyfromid] = 2
AND [currencytoid] = 5 )
ORDER BY id DESC) AS Rate
WHERE rownumber = 2)

Increment each field in a column by 1 - SQL Server 2008

I need to increment each field in a column by 1 starting from 2866. I've built the following query, but all it does is set each field to 2867 as opposed to 2868, 2869 etc...
DECLARE #a int
SET #a = 2866
UPDATE view_kantech_matched SET image_id = #a + 1
WHERE image_id = null
Any suggestions? Thanks
The general structure for doing this would be:
;WITH Numbered as (
SELECT *,ROW_NUMBER() OVER (ORDER BY <some column>) rn
FROM view_kantech_matched
WHERE image_id is NULL
)
UPDATE Numbered SET image_id = 2865 + rn
But I don't know what <some column> would be.
DECLARE #a int
SET #a = 2866
UPDATE view_kantech_matched SET image_id = #a, #a=#a + 1
WHERE image_id = null
If you want to update it to a value that starts at 2866 and increaments with every row you have to specify the column(s) to be used for the ordering.
Then you can use ROW_NUMBER:
WITH NoImageIDS AS
(
SELECT vkm.*, RN = ROW_NUMBER() OVER (ORDER BY AnyColumn ASC)
FROM dbo.view_kantech_matched vkm
WHERE image_id IS NULL
)
UPDATE NoImageIDS SET image_id = RN + 2866