Transaction was deadlocked on lock resources while reading and inserting - sql

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

Related

How to calculate no. of days between two datetime column in SQL Server

There are two tables, T1 includes start date column and T2 includes end date column. Both the columns have both date and time.
T1 has all the rows unique while T2 can have multiple rows for same id and in all the row the end date column might be different (each row with different date and time).
I want to calculate the difference (no. of days) between End date and the start date while keeping in mind that we have to only pick the last date which is lying in the End date column.
;WITH MaxEndDate AS
(
SELECT
T2.PrimaryKey,
MaxEndDate = MAX(EndDate)
FROM
T2
GROUP BY
T2.PrimaryKey
)
SELECT
T1.PrimaryKey,
T1.StartDate,
M.MaxEndDate,
AmountDays = DATEDIFF(DAY, T1.StartDate, M.MaxEndDate)
FROM
T1
INNER JOIN MaxEndDate AS M ON T1.PrimaryKey = M.PrimaryKey
I would recommend creating a temporary table using T2 something like
Select Distinct ID
,MAX(ENDDATE) As [ENDDATE]
INTO #TMPTABLE1
then you include this into T1
Select A.ID
,Startdate
,B.ENDDATE
,Datediff(day,A.STARTDATE,B.ENDDATE) as DAYS
From T1 as A inner join
#TEMPTABLE1 as B on A.ID = B.ID
--------get no of days among multiple dates----------
DECLARE #i INT , #numrows INT , #days INT , #Fdate DATETIME , #Tdate DATETIME;
SET
#days = 0;
DECLARE #employee_table TABLE ( idx SMALLINT PRIMARY KEY IDENTITY(1,
1) ,
EmpId INT ,
SinceDate DATETIME ,
TillDate DATETIME );
-- populate employee table INSERT #employee_table
SELECT
EmpId ,
SinceDate ,
TillDate
FROM
T_EmpPosting_PIS
WHERE
EmpId = 18
AND OfficeTypeID = 1
ORDER BY
TransDate;
--SELECT
*
FROM
#employee_table -- enumerate the table
SET
#i = 1;
SET
#numrows = ( SELECT
COUNT(*)
FROM
#employee_table );
IF #numrows > 0 WHILE ( #i <= ( SELECT
MAX(idx)
FROM
#employee_table ) ) BEGIN
SET
#Fdate = ( SELECT
SinceDate
FROM
#employee_table
WHERE
idx = #i );
IF ( #i = #numrows ) BEGIN
SET
#Tdate = GETDATE();
END;
ELSE BEGIN
SET
#Tdate = ( SELECT
TillDate
FROM
#employee_table
WHERE
idx = #i );
END;
SET
#days = ( SELECT
DATEDIFF(DAY,
#Fdate,
#Tdate) ) #days;
SET
#i = #i 1;
END;
SELECT
#days;

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)

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)

Select non-existing rows

Let say I have a table:
ColumnA ColumnB
---------------------------------
1 10.75
4 1234.30
6 2000.99
How can I write a SELECT query that will result in the following:
ColumnA ColumnB
---------------------------------
1 10.75
2 0.00
3 0.00
4 1234.30
5 0.00
6 2000.99
You can use a CTE to create a list of numbers from 1 to the maximum value in your table:
; with numbers as
(
select max(ColumnA) as nr
from YourTable
union all
select nr - 1
from numbers
where nr > 1
)
select nr.nr as ColumnA
, yt.ColumnB
from numbers nr
left join
YourTable yt
on nr.nr = yt.ColumnA
order by
nr.nr
option (maxrecursion 0)
See it working at SQL Fiddle.
Please try:
declare #min int, #max int
select #min=MIN(ColumnA), #max=MAX(ColumnA) from tbl
select
distinct number ColumnA,
isnull(b.ColumnB, 0) ColumnB
from
master.dbo.spt_values a left join tbl b on a.number=b.ColumnA
where number between #min and #max
Create a TallyTable (or NumbersTable) - see this question: What is the best way to create and populate a numbers table?
With that table create an insert statement:
INSERT INTO YourTable (ColumnA, ColumnB)
SELECT Number FROM NumberTable
WHERE
NOT EXISTS (SELECT 1 FROM YourTable WHERE NumberTable.Number = YourTable.ColumnA)
-- Adjust this value or calculate it with a query to the maximum of the source table
AND NumberTable.Number < 230130
DECLARE #t TABLE (ID INT,Val DECIMAL(10,2))
INSERT INTO #t (ID,Val) VALUES (1,10.75)
INSERT INTO #t (ID,Val) VALUES (4,6.75)
INSERT INTO #t (ID,Val) VALUES (7,4.75)
declare #MinNo int
declare #MaxNo int
declare #IncrementStep int
set #MinNo = 1
set #MaxNo = 10
set #IncrementStep = 1
;with C as
(
select #MinNo as Num
union all
select Num + #IncrementStep
from C
where Num < #MaxNo
)
select Num,
CASE WHEN Val IS NOT NULL THEN Val ELSE 0.00 END AS NUMBER
from C
LEFT JOIN #t t
ON t.ID = c.Num
You could use a number-table or following trick to generate a sequence which you can LEFT OUTER JOIN with your table. I assume you want to determine the boundaries dynamically:
WITH Seq AS
(
SELECT TOP ((SELECT Max(ColumnA)FROM Table1) - (SELECT Min(ColumnA) FROM Table1) + 1)
Num = (SELECT Min(ColumnA) FROM Table1)+ Row_number() OVER (ORDER BY [object_id]) -1
FROM sys.all_objects)
SELECT ColumnA = Seq.Num,
ColumnB = COALESCE(t.ColumnB ,0.00)
FROM Seq
LEFT OUTER JOIN Table1 t
ON Seq.Num = t.ColumnA
Demo with your sample.
Worth reading: http://www.sqlperformance.com/2013/01/t-sql-queries/generate-a-set-1
I have my collect of table functions like these.
create function dbo.GetNumbers(#Start int, #End int)
returns #Items table
(
Item int
)
as
begin
while (#Start <= #End)
begin
insert into #Items
values (#Start)
set #Start = #Start + 1
end
return
end
Then I can use it to left join to my data table and every value will be there.
declare #min int, #max int
set #min = 10
set #max = 20
select gn.Item
from dbo.GetNumbers(#min, #max) gn
I have similar table functions for date ranges, times, timezones, etc.

How to speed up an insert transaction being repeated over and over

I have created 2 temp tables, #Table1 being the source and #Table2 being the output table. I have to traverse through each row on #Table1 to make the necessary calculations before inserting each row into #Table2 because the calculation is also dependent on previous rows in #Table2.
I have all the rows in the order I want, and have all the calculations working perfectly. The only problem is that when I go through my 30,000 rows of test data, it takes over 30 minutes. Which means when I expand the query to the full scope of over 2,000,000 rows it will take far too long.
Example Tables:
My solution on traversing through each record in #Table1 to come up with #Table2 is provided below.
exec [database_name].dbo.drop_if_exists '#Table2'
create table #Table2 (ID int, Shipment varchar(8), Delivery int, Arrival datetime, ETA datetime, ETA_TimeStamp datetime, Compliant int)
declare #z int
declare #currentShipment varchar(8)
declare #currentStop int
declare #previousID int
declare #previousETA datetime
set #z = 1
while #z <= (Select max(ID) from #Table1)
Begin
Set #currentShipment = (Select Shipment from #Table1 where ID = #z)
Set #currentStop = (Select Delivery from #Table1 where ID = #z)
Set #previousID = (Select max(ID) from #Table2 where Shipment = #currentShipment AND Delivery = #currentStop AND ID < #Z AND Compliant = 1)
Set #previousETA = (Select ETA from #Table2 where ID = #previousID)
if (Select count(Shipment) from #Table2 where Shipment = #currentShipment AND Delivery = #currentStop AND ID < #Z AND Compliant = 1) = 0
BEGIN
insert into #Table2 Select ID
, Shipment
, Delivery
, Arrival
, ETA
, ETA_Timestamp
, Compliant_Prelim as [Compliant]
from #Table1
where ID = #Z
END
if (Select count(Shipment) from #Table2 where Shipment = #currentShipment AND Delivery = #currentStop AND ID < #Z AND Compliant = 1) > 0
BEGIN
insert into #Table2 Select ID
, Shipment
, Delivery
, Arrival
, ETA
, ETA_Timestamp
, case when (Select Compliant_Prelim from #Table1 where ID = #z) = 1
AND ETA_TimeStamp <= dateadd(hh, -1, #previousETA)
then 1
else 0 end as [Compliant]
from #Table1
where ID = #Z
END
Set #z = #z + 1
End
Select * from #Table2
How do I speed up the insert statement given this example?
The performance isn't in the "insert" itself. It is in the queries used to create the data (the select part of the insert). To fix this, consider the following.
Make sure that the id column in #table1 has an index:
create index on #table1(id);
And make sure that #table2(shipment) has an index:
create index on #table2(shipment);