sql select into select in function - sql

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)

Related

update sql result set

declare #tbl as table
(
ItemId int,
SOQty int,
DIQty int ,
IssueQty int,
BalanceQty int,
CreateDate datetime,
StockQty int
)
insert into #tbl
values (2, 5, 5, 1, 4, '2021-12-16 19:28:05.200', 80),
(1, 10, 10, 0,10, '2021-12-16 19:28:32.200', 10),
(1, 15, 10, 10,5, '2021-12-16 19:28:34.200', 10),
(1, 8, 5, 2, 3, '2021-12-16 19:28:35.200', 10)
There are 2 UPDATE statements both update on given condition
update x
set x.StockQty = tx.StockQty
from #tbl x
join
(select *
from
(select
*,
row_number() over (partition by itemid order by CreateDate) as RowNo
from #tbl) as t
where t.RowNo = 1) as tx on tx.CreateDate = x.CreateDate
update x
set x.StockQty = 0
from #tbl x
join
(select *
from
(select
*,
row_number() over (partition by itemid order by CreateDate) as RowNo
from #tbl) as t
where t.RowNo != 1) as tx on tx.CreateDate = x.CreateDate
I want above query in single updatable statement based on RowNo
condition if RowNo=1 then x.StockQty = tx.StockQty else x.StockQty =
0
update x set x.StockQty = case when tx.RowNo = 1 then x.StockQty = tx.StockQty else x.StockQty end from #tbl x
join
(select * from
(
select *,ROW_NUMBER()over(partition by itemid order by CreateDate) as RowNo from #tbl
)as t where t.RowNo = 1) as tx on tx.CreateDate = x.CreateDate
I want a single update statement and I tried so far
You can use a CTE and update the CTE:
With rankedResults
As (
Select *
, rn = row_number() over (partition by t.itemid order by t.CreateDate)
From #tbl t
)
Update rankedResults
Set StockQty = 0
Where rn > 1;
It looks like you are over-complicating it, you can just update the derived table directly
UPDATE t
SET StockQty = 0
FROM (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY CreateDate) as RowNo
FROM #tbl
) AS t
WHERE t.RowNo > 1;
db<>fiddle
Actually your first update query doing nothing, but as you have wanted I think this query below suited your scenario:
update x
set x.StockQty = CASE WHEN ISNULL(tx.RowNo,0) = 1 THEN tx.StockQty ELSE 0 END
from #tbl x
left join
(select *
from
(select
*,
row_number() over (partition by itemid order by CreateDate) as RowNo
from #tbl) as t
where t.RowNo = 1) as tx on tx.CreateDate = x.CreateDate
left join
(select *
from
(select
*,
row_number() over (partition by itemid order by CreateDate) as RowNo
from #tbl) as t
where t.RowNo != 1) as tx2 on tx2.CreateDate = x.CreateDate

Transaction was deadlocked on lock resources while reading and inserting

normally it works fine (it recalculates time to time production based on daily meter readings , the problem starts when other apps are inserting data into [dbo].[MeterReading] ( at least I belive this is the source of deadlocks), what would you reccomend to avoid exceptions ?
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare #ResultVar float;
DECLARE #Val1 TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
InputId int,
TimeStampUtc1 smalldatetime,
Val1 float
);
DECLARE #Val2 TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
InputId int,
TimeStampUtc2 smalldatetime,
Val2 float
);
WITH CTE
AS
(
SELECT [Val],m.InverterInputId, m.[TimeStampUtc]
,ROW_NUMBER() OVER(PARTITION BY m.InverterInputId ORDER BY m.[TimeStampUtc] DESC) AS RN
FROM [dbo].[MeterReading] AS m
inner join InverterInput AS ii on m.InverterInputId = ii.InverterInputId
inner join Inverter AS i on ii.InverterId = i.InverterId
where ii.InputName = 'TotalYield' and i.PlantId = #plantId and m.[TimeStampUtc] >= #from and m.[TimeStampUtc] <= #to
)
INSERT INTO #Val1 (Val1,InputId,TimeStampUtc1 ) Select [Val],[InverterInputId],[TimeStampUtc] FROM CTE WHERE RN = 1;
WITH CTE
AS
(
SELECT [Val],m.InverterInputId, m.[TimeStampUtc]
,ROW_NUMBER() OVER(PARTITION BY m.InverterInputId ORDER BY m.[TimeStampUtc] ASC) AS RN
FROM [dbo].[MeterReading] AS m
inner join InverterInput AS ii on m.InverterInputId = ii.InverterInputId
inner join Inverter AS i on ii.InverterId = i.InverterId
where ii.InputName = 'TotalYield' and i.PlantId = #plantId and m.[TimeStampUtc] >= #from and m.[TimeStampUtc] <= #to
)
INSERT INTO #Val2 (Val2,InputId,TimeStampUtc2) Select [Val],[InverterInputId],[TimeStampUtc] FROM CTE WHERE RN = 1;
--select Val1 , Val2, Val1-Val2 as Result, [#Val1].InputId,[#Val1].TimeStampUtc1,[#Val2].TimeStampUtc2 from #Val1
select Sum(Val1-Val2) as Result from #Val1
inner join #Val2 on [#Val1].InputId = [#Val2].InputId
END

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)

Selecting Spicific data placed in the middle of the database table

how to determine which row number in my database will the selection query start to select and the Limit of the selection ,as I want to select piece of data which is located in the middle of my database table
I may use between #indexOfSelection , #limitOfSelection or something like that ,but I don't know how !
CREATE PROCEDURE ordered_articles
#LowerBound int,
#UpperBound int
AS
select * from orderedData where articleid between LowerBound and UpperBound ;
with orderedData
(
select * , rn = ROW_NUMBER() over ORDER BY (articleid)
from articles
)
WHERE rn >= #LowerBound AND rn <= #UpperBound
RETURN
I guess you can make use of ROW_NUMBER Function something like this ....
;WITH OrderedData
AS
(
SELECT * , rn = ROW_NUMBER() OVER (ORDER BY SomeColumn)
FROM Table_Name
)
SELECT * FROM OrderedData
WHERE rn >= #LowerLimit AND rn <= #UpperLimit
Your Query
select * from articles
where articleid between #indexOfSelection AND #LimitOfselection
You just need to add the key word AND between your upper lower limit variable and upper limit variable.
Your Stored Procedure
CREATE PROCEDURE ordered_articles
#LowerBound int,
#UpperBound int
AS
BEGIN
SET NOCOUNT ON;
select * from articles
where articleid between #LowerBound and #UpperBound
END
To Select A range Of Rows
CREATE PROCEDURE ordered_articles
#LowerBound int,
#UpperBound int
AS
BEGIN
SET NOCOUNT ON;
WITH OrderedData
AS
(
SELECT * , rn = ROW_NUMBER() OVER (ORDER BY articleid)
FROM articles
)
SELECT * FROM OrderedData
WHERE rn >= #LowerBound AND rn <= #UpperBound
END
EXECUTE ordered_articles 10, 15 --<-- this will return 10 to 15 number row ordered by ArticleID

Should I use APPLY in this context

Code included is a simplified version of our situation; the production table equivalent to #MyExample has 20 fields all of which need medians calculating therefore the second part of the script becomes very long - not a huge hard-ship but is there a more compact solution?
I've no experience with APPLY or custom FUNCTIONs but is this a situation where we should create a FUNCTION for the median and then use APPLY I'm guessing not as apply is applied to each row?
/*
DROP TABLE #MyExample
DROP TABLE #mediantable
*/
CREATE TABLE #MyExample
(
customer char(5),
amountPeriodA numeric(36,8),
amountPeriodB numeric(36,8),
amountPeriodC numeric(36,8)
)
INSERT INTO #MyExample
values
('a',10,20,30),
('b',5,10,15),
('c',500,100,150),
('d',5,1,1),
('e',5,1,15),
('f',5,10,150),
('g',5,100,1500)
SELECT
[Period] = 'amountPeriodA',
[Median] = AVG(x.amountPeriodA)
INTO #mediantable
FROM (
SELECT
r.customer,
r.amountPeriodA,
[RowASC] = ROW_NUMBER() OVER(ORDER BY r.amountPeriodA ASC, customer ASC),
[RowDESC] = ROW_NUMBER() OVER(ORDER BY r.amountPeriodA DESC, customer DESC)
FROM #MyExample r
) x
WHERE RowASC IN (RowDESC, ROWDESC-1, ROWDESC+1)
union
SELECT
[Period] = 'amountPeriodB',
[Median] = AVG(x.amountPeriodB)
FROM (
SELECT
r.customer,
r.amountPeriodB,
[RowASC] = ROW_NUMBER() OVER(ORDER BY r.amountPeriodB ASC, customer ASC),
[RowDESC] = ROW_NUMBER() OVER(ORDER BY r.amountPeriodB DESC, customer DESC)
FROM #MyExample r
) x
WHERE RowASC IN (RowDESC, ROWDESC-1, ROWDESC+1)
union
SELECT
[Period] = 'amountPeriodC',
[Median] = AVG(x.amountPeriodC)
FROM (
SELECT
r.customer,
r.amountPeriodC,
[RowASC] = ROW_NUMBER() OVER(ORDER BY r.amountPeriodC ASC, customer ASC),
[RowDESC] = ROW_NUMBER() OVER(ORDER BY r.amountPeriodC DESC, customer DESC)
FROM #MyExample r
) x
WHERE RowASC IN (RowDESC, ROWDESC-1, ROWDESC+1)
SELECT *
FROM #mediantable
Building on my previous reply I arrived on this which is a lot easier (and shorter) to expand for the number of columns and even runs a bit faster (probably a lot faster in case of 20+ columns!). However, it returns the results horizontally instead of vertically. This can be 'solved' again using UNPIVOT.
I've done the operation in 2 parts using an intermediate #result table; but you could easily do it in a single statement using a subquery or CTE.
DECLARE #rowcount int
DECLARE #first int
DECLARE #last int
DECLARE #divider numeric(36,8)
SELECT #rowcount = COUNT(*) FROM #MyExample
SELECT #first = (CASE WHEN #rowcount % 2 = 1 THEN (#rowcount + 1) / 2 ELSE (#rowcount / 2) END),
#last = (CASE WHEN #rowcount % 2 = 1 THEN (#rowcount + 1) / 2 ELSE (#rowcount / 2) + 1 END),
#divider = (CASE WHEN #rowcount % 2 = 1 THEN 1 ELSE 2 END)
SELECT amountPeriodA = SUM(amountPeriodA) / #divider,
amountPeriodB = SUM(amountPeriodB) / #divider,
amountPeriodC = SUM(amountPeriodC) / #divider
INTO #result
FROM
(
SELECT amountPeriodA = ((CASE WHEN ROW_NUMBER() OVER(ORDER BY amountPeriodA ASC, customer ASC) IN (#first, #last) THEN amountPeriodA ELSE 0.00 END)),
amountPeriodB = ((CASE WHEN ROW_NUMBER() OVER(ORDER BY amountPeriodB ASC, customer ASC) IN (#first, #last) THEN amountPeriodB ELSE 0.00 END)),
amountPeriodC = ((CASE WHEN ROW_NUMBER() OVER(ORDER BY amountPeriodC ASC, customer ASC) IN (#first, #last) THEN amountPeriodC ELSE 0.00 END))
FROM #MyExample
)t
and then
SELECT [Period], [Amount]
FROM #result as x
UNPIVOT ( [Amount] FOR Period IN (amountPeriodA, amountPeriodB, amountPeriodC)) As unpvt
I was thinking along the lines of :
DECLARE #rowcount int
DECLARE #first int
DECLARE #last int
SELECT #rowcount = COUNT(*) FROM #MyExample
SELECT #first = (CASE WHEN #rowcount % 2 = 1 THEN (#rowcount + 1) / 2 ELSE (#rowcount / 2) END),
#last = (CASE WHEN #rowcount % 2 = 1 THEN (#rowcount + 1) / 2 ELSE (#rowcount / 2) + 1 END)
SELECT [Period],
[Median] = AVG(Amount)
FROM (SELECT [Period] = 'amountPeriodA',
Amount = amountPeriodA,
rownbr = ROW_NUMBER() OVER(ORDER BY amountPeriodA ASC, customer ASC)
FROM #MyExample
UNION ALL
SELECT [Period] = 'amountPeriodB',
Amount = amountPeriodB,
rownbr = ROW_NUMBER() OVER(ORDER BY amountPeriodB ASC, customer ASC)
FROM #MyExample
UNION ALL
SELECT [Period] = 'amountPeriodC',
Amount = amountPeriodC,
rownbr = ROW_NUMBER() OVER(ORDER BY amountPeriodC ASC, customer ASC)
FROM #MyExample
) r
WHERE rownbr IN (#first, #last)
GROUP BY [Period]
Which seems to work well, is a bit less typing and turns out to be a bit faster too.... but it's still 'big'.
PS: Use UNION ALL rather than UNION as otherwise the server will try to make the end-result into 'distinct' records which in this case is not needed. (Period makes it unique anyway !)