SELECT with CASE after UPDATE uses old value - sql

I have a problem with a combination of sql statements generated by entity-framework.
It looks like the updated value of the powerloss field is not immediately set after the update statement completes and the select gets executed.
I don't know if that's even possible.
Maybe I'm just missing something.
The updated and selected rows and the data in the table after the code executes are correct.
What it should do (and it does it 99% of the time)
I get a counterValue from a device and insert it into a table on a sql-server 2008 R2 (SP3). If the device has lost power and at some point is back, the field "powerloss" in the last counterValue entry in the database will be updated to 1(true). By default, the field is 0(false).
After that, I query the last counterValue of this device and negate it.
But only if the field "powerloss" is 0(false). Otherwise the query must return zero.
What goes wrong
Sometimes (very rare) I get the negated counterValue when the powerloss field was updated to 1(true) right before the select...
That's at least what my log-file shows me (every query is logged).
What the code does
Create dbContext
BeginTransaction(isolationLevel.ReadComitted)
WriteNewCounterValue (after Powerloss)
Update field powerloss=true
Select counterValue depend on value in powerloss field
offset = (From qItem In DB.SlaveCounterEntries
Where qItem.deviceId= deviceId
And qItem.Received < timestamp
Order By qItem.Received Descending
Select If(qItem.Powerloss = True, 0, -qItem.CounterValue)
Take 1).SingleOrDefault()
Insert new counterValue in Table
Commit Transaction
Dispose dbContext
The gernerated sql statements
The update statement:
UPDATE [dbo].[slaveCounter]
SET [powerloss] = #0
WHERE ([id] = #1)
-- #0: 'True' (Type = Boolean)
-- #1: '3371747' (Type = Int32)
-- Executing at 29.06.2018 05:57:24 +02:00
-- Completed in 0 ms with result: 1
The select statement (14 ms after the update):
SELECT TOP (1)
[Project1].[C1] AS [C1]
FROM ( SELECT
CASE WHEN (1 = [Extent1].[powerloss]) THEN 0 ELSE -([Extent1].[counterValue]) END AS [C1],
[Extent1].[datReceived] AS [datReceived]
FROM [dbo].[slaveCounter] AS [Extent1]
WHERE ([Extent1].[slaveId] = #p__linq__0) AND ([Extent1].[datReceived] < #p__linq__1)
) AS [Project1]
ORDER BY [Project1].[datReceived] DESC
-- p__linq__0: '48' (Type = Int16, IsNullable = false)
-- p__linq__1: '28.06.2018 23:00:03' (Type = DateTime2, IsNullable = false)
-- Executing at 29.06.2018 05:57:24 +02:00
-- Completed in 0 ms with result: SqlDataReader

I found the reason why it seems that the update statement does not work:
An other transaction added a second record which had the same counterValue and was not updated and later be deleted.
So only the "correct" entry remains.
In retrospect, everything seems so clear ^^

Related

Reusing calculated column in update in same code-block

I am trying to find the (best) way to update column of table based on calculated another column.
First of all, I am using function to calculate value for 'BAS_CBR_EB_RWA' column and then I am using 'BAS_CBR_EB_RWA' value as an input for 'BAS_CBR_EB_TOTAL_CAPITAL' column calculation, as shown in below-mentioned code.
Could you please share how to reuse it to do next calculation. replacing 'BAS_CBR_EB_RWA' with right-hand calculation isn't desirable because we have too many calculation of this type and it'll confuse other users.
Thanks in advance for help.
IF INSTTABLE = 16 THEN
UPDATE LAO_DATA
SET BAS_CBR_EB_RWA = BAS2_RWA_CALC(BAS_CAPITAL_CALC_CD,
CBR_CUR_BOOK_BAL,
BAS_CAP_FACTOR_K,
V_BASEL_MIN,
V_BAS_RWA_RATE),
BAS_CBR_EB_TOTAL_CAPITAL = ROUND(BAS2_MGRL_CAPITAL(V_DATE,
BAS_CBR_EB_RWA,
0),
2),
WHERE (AS_OF_DATE = V_DATE);
--COMMIT;
END IF;
In Oracle, you can update a subquery. I'm not 100% sure if it works for UDFs, but you can try:
UPDATE (SELECT LD.*,
BAS2_RWA_CALC(BAS_CAPITAL_CALC_CD, CBR_CUR_BOOK_BAL, BAS_CAP_FACTOR_K, V_BASEL_MIN, V_BAS_RWA_RATE) as new_BAS_CBR_EB_RWA
FROM LAO_DATA LD
)
SET BAS_CBR_EB_RWA = new_BAS_CBR_EB_RWA,
BAS_CBR_EB_TOTAL_CAPITAL = ROUND(BAS2_MGRL_CAPITAL(V_DATE, nw_BAS_CBR_EB_RWA, 0), 2),
WHERE AS_OF_DATE = V_DATE;
A MERGE statement can be used. You may also replace ROWID with the primary key or a Unique key of the table.
Put all your first level of calculations with function calls inside the USING() block and the second level of calculation for RHS in the SET expression
MERGE INTO lao_data t
USING (
SELECT ROWID AS rid,bas2_rwa_calc(bas_capital_calc_cd,
cbr_cur_book_bal,
bas_cap_factor_k,
v_basel_min,v_bas_rwa_rate
) AS new_BAS_CBR_EB_RWA
FROM lao_data
WHERE as_of_date = V_DATE
)
s ON ( s.rid = t.rowid )
WHEN MATCHED THEN UPDATE
SET t.bas_cbr_eb_rwa = s.new_BAS_CBR_EB_RWA
t.bas_cbr_eb_total_capital
= round(bas2_mgrl_capital(v_date,s.nw_BAS_CBR_EB_RWA,0), 2) );

SQL : Update value when the value in the database is null

I know this is already asked question and possible to be close.
But i really want a answer, I already searched through the internet, Read documentations, Blogs, and Question to SO.
This is my Query so Far,
declare #count numeric
select #count = (select count(1) from E496_TitleReference a where
exists (select 1 from #tempTransactions b where a.EPEB_RoD = b.tEPEB_RoD and
a.EPEB_ENO = b.tEPEB_ENO and a.EPEB_ID = b.tEPEB_ID and a.Title_Seq = b.tTitle_Seq))
update E496_TitleReference
set PrintStatus = '{0}',Is_AESM=isnull(-1,Is_AESM)
from E496_TitleReference a where
exists (select 1 from #tempTransactions b where a.EPEB_RoD = b.tEPEB_RoD and
a.EPEB_ENO = b.tEPEB_ENO and a.EPEB_ID = b.tEPEB_ID and a.Title_Seq = b.tTitle_Seq)
if ##rowcount <> #count
begin
rollback tran
Print "Error: There is an error on table E496_TitleReference."
return
end
go
For eg, In my table in Database i have column name Is_AESM, In Is_AESM column it have 4 values.
Is_AESM
NULL
NULL
-1
-2
Something like this.
Now when i run my script, it has no problem when i run it,
Is_AESM=isnull(-1,Is_AESM)
In this query it will detect if Is_AESM is null, it will update Is_AESM = -1 if not it will retain the value.
Now my problem is, if my query detect Is_AESM has a null value, it will update all the value to -1.
Is_AESM
-1
-1
-1
-1
The result is something like that. Now i want is update only the null value not all the value in column Is_AESM.
I think this query is wrong Is_AESM=isnull(-1,Is_AESM).
Any ideas will be a big help.
You may try with coalsece() function
update E496_TitleReference
set PrintStatus = '{0}',Is_AESM=coalsece(Is_AESM,-1)
from E496_TitleReference a where
exists (select 1 from #tempTransactions b where a.EPEB_RoD = b.tEPEB_RoD and
a.EPEB_ENO = b.tEPEB_ENO and a.EPEB_ID = b.tEPEB_ID and a.Title_Seq = b.tTitle_Seq)
you need to replace order of parameters.
Is_AESM=isnull(Is_AESM, -1)
You can use COALSECE function. It returns the first non-null entry from the given list. So:
Is_AESM= COALSECE(IS_AESM,-1)
This will return IS_AESM value if it is not null (since it is the first non-null value)
Else if IS_AESM is NULL then it returns -1 (since it is the non-null value)

Specifying an upper bound for an int in UPDATE

Given a table, for example Article(Id,Body,Revisions), I would like to increment the Revisions attribute, and, once a certain limit is reached (it's a constant provided by the developer), an error should be thrown. Is this possible to achieve with a single UPDATE ... SET statement in T-SQL?
What I've done:
To increment Revisions attribute by one, I solved as shown here: Is UPDATE command thread safe (tracking revisions) in MS SQL.
Problem
To find a way that is thread safe, which would allow incrementation of Revisions until a certain upper bound is reached.
Context
Since I'm using EF, the ideal solution would be to either thrown an error or specify a flag of some sort. The code I'm using (shown below) is encapsulated into a try-catch:
context.Database.ExecuteSqlCommand("UPDATE dbo.Articles SET Revisions = Revisions + 1 WHERE Id=#p0;", articleId);
You could do this with a WHERE clause in your UPDATE statement, which would do the test. If the test fails, the update will not happen and your call with context.Database.ExecuteSqlCommand will return 0 instead of 1.
In case of a limit of 1000, the update SQL would be:
count = context.Database.ExecuteSqlCommand(
"UPDATE dbo.Articles SET Revisions = Revisions + 1 WHERE Id=#p0 AND Revisions < 1000;", articleId);
Then afterwards you would test whether count == 0 and raise an error message if so.
Use a CHECK constraint. No update statement can violate bounds that are implemented by a CHECK constraint. Not even an update statement issued by a sleep-deprived DBA at the console.
create table article (
id integer primary key,
body nvarchar(max) not null,
-- Allow six versions. (Original plus five revisions.)
revisions integer not null
check (revisions between 0 and 5)
);
insert into article values (1, 'a', 0);
update article
set body = 'b', revisions = 1
where id = 1;
update article
set body = 'c', revisions = 2
where id = 1;
-- Other updates . . .
-- This update will *always* fail with an error.
update article
set body = 'f', revisions = 6
where id = 1;
Kind of whacked but
UPDATE dbo.Articles
SET Revisions = Revisions + 1
WHERE Id=#p0
AND sqrt(Revisions - #MaxRevisions - 2) >= 0;
If Revisions - #Revisions - 2 is negative it will throw an
An invalid floating point operation occurred.
error
If the limit is 100,
UPDATE Article
SET Revisions = LEAST(Revisions + 1, 100)
where Id = #p0

Stored procedure to find next and previous row in SQL Server 2005

Right now I have this code to find next and previous rows using SQL Server 2005. intID is the gallery id number using bigint data type:
SQL = "SELECT TOP 1 max(p.galleryID) as previousrec, min(n.galleryID) AS nextrec FROM gallery AS p CROSS JOIN gallery AS n where p.galleryid < '"&intID&"' and n.galleryid > '"&intID&"'"
Set rsRec = Server.CreateObject("ADODB.Recordset")
rsRec.Open sql, Conn
strNext = rsRec("nextrec")
strPrevious = rsRec("previousrec")
rsRec.close
set rsRec = nothing
Problem Number 1:
The newest row will return nulls on the 'next record' because there is none. The oldest row will return nulls because there isn't a 'previous record'. So if either the 'next record' or 'previous record' doesn't exist then it returns nulls for both.
Problem Number 2:
I want to create a stored procedure to call from the DB so intid can just be passed to it
TIA
This will yield NULL for previous on the first row, and NULL for next on the last row. Though your ordering seems backwards to me; why is "next" lower than "previous"?
CREATE PROCEDURE dbo.GetGalleryBookends
#GalleryID INT
AS
BEGIN
SET NOCOUNT ON;
;WITH n AS
(
SELECT galleryID, rn = ROW_NUMBER()
OVER (ORDER BY galleryID)
FROM dbo.gallery
)
SELECT
previousrec = MAX(nA.galleryID),
nextrec = MIN(nB.galleryID)
FROM n
LEFT OUTER JOIN n AS nA
ON nA.rn = n.rn - 1
LEFT OUTER JOIN n AS nB
ON nB.rn = n.rn + 1
WHERE n.galleryID = #galleryID;
END
GO
Also, it doesn't make sense to want an empty string instead of NULL. Your ASP code can deal with NULL values just fine, otherwise you'd have to convert the resulting integers to strings every time. If you really want this you can say:
previousrec = COALESCE(CONVERT(VARCHAR(12), MIN(nA.galleryID)), ''),
nextrec = COALESCE(CONVERT(VARCHAR(12), MAX(nB.galleryID)), '')
But this will no longer work well when you move from ASP to ASP.NET because types are much more explicit. Much better to just have the application code be able to deal with, instead of being afraid of, NULL values.
This seems like a lot of work to get the previous and next ID, without retrieving any information about the current ID. Are you implementing paging? If so I highly recommend reviewing this article and this follow-up conversation.
Try this (nb not tested)
SELECT TOP 1 max(p.galleryID) as previousrec, min(n.galleryID) AS nextrec
FROM gallery AS p
CROSS JOIN gallery AS n
where (p.galleryid < #intID or p.galleryid is null)
and (n.galleryid > #intID or n.galleryid is null)
I'm assuming you validate that intID is an integer before using this code.
As for a stored procedure -- are you asking how to write a stored procedure? If so there are many tutorials which are quite good on the web.
Since Hogan contributed with the SQL statement, let me contribute with the stored proc part:
CREATE PROCEDURE spGetNextAndPreviousRecords
-- Add the parameters for the stored procedure here
#intID int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT TOP 1 max(p.galleryID) as previousrec, min(n.galleryID) AS nextrec
FROM gallery AS p
CROSS JOIN gallery AS n
where (p.galleryid < #intID or p.galleryid is null)
and (n.galleryid > #intID or n.galleryid is null)
END
And you call this from code as follows (assuming VB.NET):
Using c As New SqlConnection(ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString)
c.Open()
Dim command = New SqlCommand("spGetNextAndPreviousRecords")
command.Parameters.AddWithValue("#intID", yourID)
Dim reader as SqlDataReader = command.ExecuteReader()
While(reader.Read())
' read the result here
End While
End Using

Visual Fox Pro 6.0 Query of Logical DataType not working!

My current system has a query to generate a tax report. The problem is that sometimes orders go into our system that never get submitted but are still counted in the tax report. The flag that sets an order as submitted is called 'complete' and it will be set to TRUE using the logical datatype.
Two issues arise from the following code. First, it seems as though the field I am using as a constraint 'complete' is a FoxPro reserved function because it lights up in blue while in FoxPro. The second problem is that it will not exclude those records that never get submitted (basically the constraint is not working).
EDITED CODE:
sele bkmast
set order to county
set filt to between(sysdate, m.ld_start, m.ld_end)
go top
m.lh_countylines = ''
select 000000.0000 as ordamt, import, county, 00000000.00 as amount, date() as start, date() as end dist;
from bkmast ;
where !empty(county) ;
.and. alltrim(county) !='0' ;
.and. alltrim(county) !='8.00_Wyoming' ;
.and. alltrim(county) !='Select County' ;
order by county ;
into table countytax
m.ln_total=0
m.ln_countamt = 0
scan
m.lc_county = alltrim(county)
sele bkmast
seek m.lc_county
sum tax to m.ln_amt while county=m.lc_county
seek m.lc_county
sum ordamt to m.ln_ordamt while county=m.lc_county
sele countytax
replace ordamt with m.ln_ordamt
replace amount with m.ln_amt
replace startDate with m.ld_start
replace endDate with m.ld_end
m.ln_countamt = m.ln_countamt + ordamt
m.ln_total = m.ln_total + amount
m.lh_countylines = m.lh_countylines+elemerge(html_frm("TAXCOUNTY1"))
endscan
Any help is greatly appreciated.
Having worked with Foxpro since FoxBase back in '87, I've never known a "complete" command, nor is it directly documented in the VFP Help (yet as stated, DOES highlight in blue as a function call via Complete() ). Additionally the .AND. is long ago old indicator of query. The "End" though IS a keyword. I would try by qualifying the columns by adding the alias to the query and changing End to EndDate (and pairing up Start to StartDate), such as...
From the result of your other comment, I would do your pre-querying directly in the select statement, then do your updates...
SELECT
bk.Import,;
bk.county,;
sum( bk.OrdAmt ) AS OrdAmt,;
sum( bk.Tax ) AS Amount,;
m.ld_Start AS startDate,;
m.ld_End AS endDate;
FROM ;
bkmast bk ;
where ;
sysdate between m.ld_start and m.ld_End;
AND NOT empty( ALLTRIM( bk.county )) ;
AND NOT alltrim( bk.county ) == '0' ;
and NOT alltrim( bk.county ) == '8.00_Wyoming' ;
and NOT alltrim( bk.county ) == 'Select County' ;
AND bk.complete;
group by ;
bk.Import,;
bk.county;
order by;
bk.county ;
into;
table countytax
In this case, since the aggregations of order amount and tax, you don't need to go back to the BKMast table... its already done... you can just cycle through the result set directly. The only thing left would be to sum up the total tax and order amounts... If those variables are not used within your elemerge(html_frm("TAXCOUNTY1")) call, you can just pre-sum those directly
select CountyTax
sum OrdAmt, Amount to m.ln_CountAmt, m.ln_Total
scan
*/ These two already summed up from before the scan loop
** m.ln_countamt = m.ln_countamt + ordamt
** m.ln_total = m.ln_total + amount
*/ Now, continue with the eleMerge() function which will already have the
*/ values in the CountyTax table already summed up
m.lc_county = alltrim(county)
m.lh_countylines = m.lh_countylines+elemerge(html_frm("TAXCOUNTY1"))
endscan
What happens if you run a query where the only this in the WHERE clause is complete:
SELECT ;
000000.0000 OrdAmt,;
bk.Import,;
bk.county,;
00000000.00 Amount,;
date() as startDate,;
date() as endDate;
from bkmast bk;
where bk.Complete ;
into cursor csrTest
Does that get the right set of records? What I'm getting at is that maybe the Complete field doesn't contain what you think it does.
Tamar