« first day (5165 days earlier)   

00:30
@SeanGallardy I was under the impression that when @LockOwner is Transaction (the default) then it behaves the same way as any other lock. So you need to use XACT_ABORT ON to guarantee rollback, otherwise you are in the same position as any other lock taken in a transaction.
I agree that when under @LockOwner='Session' then you need to very carefully consider your backout code, in particular how connection pooling plays with it. But not sure why you'd ever use that option anyway.
Also that you do need to check the result of the EXEC for the error conditions (timeout, deadlock, cancellation), and it would be nicer if those errors just threw as normal.
00:45
You can set a lock timeout via the @LockTimeout parameter
But don’t mind me
I’ll be over here doing just fine without anyone asking
 
2 hours later…
02:29
@JohnK.N. Looks like 4.972% (!)
 
3 hours later…
05:53
@Charlieface Moreover, you can only acquire an app lock inside a transaction. When that transaction ends, the lock goes away.
(session scoped app locks are extremely rare)
06:05
16
A: Handling concurrent access to a key table without deadlocks in SQL Server

A-KFirst, I would avoid making a round trip to the database for every value. For example, if your application knows it needs 20 new IDs, do not make 20 round trips. Make only one stored procedure call, and increment the counter by 20. Also it might be better to split your table into multiple ones. ...

It is my first choice because I have researched and stress tested it only once, and I can reuse it in any situation - the serializing has already happened, so everything that happens after sp_getapplock does not affect the outcome. With standard locking mechanisms, I can never be so sure - adding an index or just getting another execution plan can cause deadlocks where before there were none. Ask me how I know. — A-K Mar 16, 2013 at 1:35
I sometimes think people advocating "normal locking mechanisms" have never dealt with a real production database
For things like ensuring a procedure is run as a singleton, it is possible to (ab)use standard locking to achieve the same result, but I think an app lock conveys the intention better.
 
4 hours later…
10:38
Thanks Sean, I look forward to reading that series.
Wordle 1,346 3/6*

⬛⬛⬛⬛⬛
⬛⬛🟩⬛🟨
🟩🟩🟩🟩🟩
11:38
Wordle 1,346 4/6*

🟨⬛🟨⬛⬛
⬛🟨🟨⬛🟨
⬛🟩🟩🟩⬛
🟩🟩🟩🟩🟩
11:52
@PaulWhite So what does @SeanGallardy mean then? Did I misunderstand something?
Wordle 1,346 4/6*

⬜⬜⬜⬜⬜
🟨🟩⬜🟩⬜
⬜🟩🟩🟩🟩
🟩🟩🟩🟩🟩
@Charlieface No idea. Perhaps he has only seen stupid uses of the feature
12:16
The answer you quoted above doesn't check the EXEC result by the way.
@PaulWhite Just having a look through your interesting article re Lazy Index Spool sql.kiwi/2025/02/lazy-index-spool
Couldn't you get the raw page data via a UDF, or maybe a loopback connection in SQLCLR?
To show the contents of the spool's internal worktable?
I mean, as I said, there are other ways, but they're quite convoluted compared with setting a trace flag
@Charlieface It does not, but (a) that's not really relevant in the question's context; and (b) which return code would you care about and how would you handle it in that specific scenario?
It's a shame sys.dm_db_page_info isn't more useful, or maybe we need a new DMV sys.dm_db_page_body_info. Then we could just apply it inside the same query.
> -1 The lock request timed out.
-2 The lock request was canceled.
-3 The lock request was chosen as a deadlock victim.
@Charlieface It's not really clear what you're suggesting. How would you know which page(s) to look at
12:31
All of those normally throw an error, rather than falling through.
The examples don't use a time out do they? Lock request cancelled is never seen. I suppose a deadlock is possible in theory, but it's not really that uncommon to omit error handling code when demoing a separate concept.
I guess. Having said that, usually the whole point of using it is to avoid a deadlock, so you have to do something really stupid (like take locks in different orders in different places) to get a deadlock.
What does it mean to "cancel" a lock anyway? As opposed to a timeout?
MichaelSwart points out that sp_releaseapplock shouldn't be used under RCSI anyway, as the COMMIT does that. michaeljswart.com/2021/01/…
@Charlieface Usually an attention call (client hits stop etc)
right, yeah you can't see that in TSQL
@Charlieface Seems like a moot point. You protect the thing that needs locking until it doesn't need locking any more. I suppose some people might be unaware that ending the transaction releases the app lock, but they should read the documentation.
People like that are the reason peanut butter has a warning about possibly containing nuts
13:09
Yes buried in the docs is the line "When a lock is opened with the Transaction lock owner, that lock is released when the transaction is committed or rolled back." But the also quite prominently show incorrect examples with sp_releaseapplock so I think the point is justified.
13:35
> Locks placed on a resource are associated with either the current transaction or the current session. Locks associated with the current transaction are released when the transaction commits or rolls back. Locks associated with the session are released when the session is logged out. When the server shuts down for any reason, all locks are released.
May contain nuts
13:54
> An interesting restatement of these requirements is that the spool can cache a maximum of one result row for each value of the correlated apply value. In other words, the correlated apply parameters are a key for the spool’s cache.
@PaulWhite should correlated apply value be plural since correlated parameters below it is?
I toyed with "parameter(s)" but adjusting the are/is as well seemed too much.
It's a bit muddled. Perhaps I will have another go. In this specific instance, there is one correlated parameter. In the general case, there might be more.
Wordle 1,346 4/6*

⬛⬛⬛⬛⬛
⬛⬛⬛⬛⬛
⬛🟩🟩🟩⬛
🟩🟩🟩🟩🟩
> An interesting restatement of these requirements is that the spool can cache a maximum of one result row for each value of the correlated apply parameter(s). In other words, the correlated apply parameters form a key for the spool’s cache.
@ErikReasonableRatesDarling Is that better
@PaulWhite I miss the inimitable Alex
14:20
@PaulWhite yes, much more clear now(s)
tyau
@@CURSOR_ROWS is kinda weird huh
May 14, 2024 at 18:21, by Hannah Vernon
DECLARE @cur CURSOR;
DECLARE @rows int;
DECLARE @db_name sysname;

SET @cur = CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY
FOR
SELECT [d].[name]
FROM [sys].[databases] [d]
ORDER BY [d].[name];

OPEN @cur;
SET @rows = @@CURSOR_ROWS;
WHILE @rows > 0
BEGIN
    FETCH NEXT FROM @cur INTO @db_name;
    PRINT @db_name;
    SET @rows -= 1;
END;
CLOSE @cur;
May 14, 2024 at 18:23, by Paul White
Yes, that's a good choice when the cursor has fixed membership
yes
kinda weird
getting hit with the -1 or 0 sometimes
all documented why of course
i feel like i'm going to be in a real cursor hole this week
Take it one row at a time
14:30
while using dm_exec_cursors the properties output looks like TSQL | Fast_Forward | Read Only | Local (0) which reminds me of your answers doing things like @scrollopt integer = 16 | 8192 | 16384 | 32768 | 1048576
perhaps it's finally my time
to become an api cursor lad
Wordle 1,346 4/6*

⬜⬜🟩🟩⬜
⬜🟩🟩🟩⬜
⬜🟩🟩🟩⬜
🟩🟩🟩🟩🟩
@ErikReasonableRatesDarling It's inevitable
what should i do first
learn batched modifications
make pagination great again
perhaps an alternative for deduplication
14:55
@ErikReasonableRatesDarling You could try predicting the output of:
USE master;

DECLARE
    @cur CURSOR;

DECLARE
    @rows integer,
    @table_name sysname,
    @name_like sysname = N'%monitor%';

SET @cur = CURSOR
    LOCAL FORWARD_ONLY DYNAMIC READ_ONLY
    FOR
    SELECT
        T.[name]
    FROM sys.tables AS T
    WHERE
        T.[name] LIKE @name_like;

SET @name_like = N'%options%';

OPEN @cur;

WHILE 1 = 1
BEGIN
    FETCH NEXT FROM @cur INTO @table_name;
    IF @@FETCH_STATUS = -1 BREAK;
    PRINT @table_name;
END;

CLOSE @cur;
15:41
well
i predict the cursor will predict -1
You'd be right about that part
16:05
e-cores spinning at full speed today
16:18
There's room for a third, LPI (low power island)
what's a low power island
The question is whether rows returned by the cursor will contain 'monitor' or 'options'
> Meteor Lake design has three types of cores: Performance Cores (P-Cores), Efficient Cores (E-Cores), both located in the Compute Tile, and Low Power Efficient Cores located in the SOC tile.
16:21
I reside on a different island, full of misfit toys
16:36
That place is something else
 
2 hours later…
18:07
is there some technical reason why you can't set fill factor for a heap
what would be the point? genuine question
I mean a heap is not an index, and fill factor specifies how much to fill up a leaf-level page of an index before creating another leaf-level page. Right?
i have a very large temp table that gets updated many times, in ways that can't be easily collapsed to the initial insert. for example, some updates depend on the results of what other updates accomplish. the query plans are all very fast up to the point of the update operator, which often takes 30+ seconds to perform its task. i would guess that a lot of this time is page splitting as non-null values are added.
i could add default values to the initial insert, but i'd have to change a bunch of downstream query logic.
it's easy enough to add a clustered index with a lower fill factor, but i like the heap a bit.
Heaps don't split pages
A row that becomes too large to fit gets forwarded
yes that
i think that's where the work is going
and then potentially slowing down the later scans of the heap
clumsy clustered index it is then i guess
18:19
Your only option with heaps is to pre-size
default values?
hmpf
what data types are your null values?
mostly bigints. some bits.
Padding, fixed data types, whatever
about 50 columns total
45 bigint, 5 bit
18:21
I was thinking you could use a default value like the minimum value for a bigint or whatever. I agree it's painful to use a magic value.
Anything would be better than null
You're using fixed length data types but then ruining it with null
Low power island detected
this isn't my design
18:24
You brought it in here
good thing your rates are reasonable
You own it
Unfortunately, his m8s are unreasonable
the best kind of mates
We do have a reputation to maintain
@ErikReasonableRatesDarling what type of table would it make sense to be doing complex updates on it and still keep it a heap?
18:26
A pre sized one you want to load with parallelism
As in, what's clumsy about a clustered index more than a heap?
Doesn't support parallel inserts
Lookups are slower
Could be worse. Could be column store
I hear NoSQL solves everything.
With regex
The other thing is that often when people add a clustered index, they also add a $#%^ identity column as well
Oh and you can't SELECT INTO a clustered table
just never do ETL
May 4, 2023 at 16:43, by Hannah Vernon
Apr 23 at 13:31, by Hannah Vernon
Computers were a mistake
18:38
One thing that does annoy me. People who think fixed length types like char are ridiculous because they can't cope with trailing spaces
that reminds me I need to re-download all the .TLD files and run that giant MERGE again.
Like they were only ever added to the product as some sort of trap
i'm going with heap on load with default values and a low fill factor unique clustered index created after
What
Also, WHAT
he said "I'm going with a heap on load with default values and a low fill factor unique clustered index created after". I hope that helps.
18:40
How does that make any sense with all fixed length data types. I mean do one or the other
Avoid nulls or play guess the fill factor
well if one is good then both must be twice as good
🥴
I see your reasoning
unassailable really
@ErikReasonableRatesDarling I thought that's what nchar was for?
18:44
unicode a tutti!
Why is every other byte 0?
what if i preload them all with a default value and then set them back to null so i don't have to change any downstream code
Because you forgot to use row compression
That could work, but it's a heck of an initial update, fully logged
18:46
Keep those bytes apart
Seriously, row compression comes with Unicode compression built in
Or roll the dice on UTF-8
Erik working hard to preserve the tech debt
This is how SQL Server became 100M LOC
Maybe you should normalise
Updates were a mistake
why wouldn't you just insert the correct data in the first place
Avoiding updates might be a good answer, I have sometimes found with levels of derived calculations in CTEs, it ends up faster to store them in new structures like yet another table - potentially indexed differently and then move onto the next set of calculations in the same way you think about it in your head, just instantiated instead of CTEs.
19:02
SQL Server isn't licenced per table
Don't give them any ideas
yeah, i suppose i could work out separate inserts instead
It's a miracle
there's just a lot of business-ruling with this type of data
insurance isn't fun
In my experience, most of the business rules are wrong
19:04
that is a solid 1m24s update
hoowee
Is that good or bad
depends
it could make up for itself if it shaves enough time off the multitude of 30+ second updates that come later
It's fundamentally a pretty horrible idea
yeah
with the clustered index all the scans are now single threaded
92... 92 messages in chat. I'm not reading any of it.
> Microsoft’s Satya Nadella Pumps the Brakes on AI Hype
"AI" doesn't make money if you're selling it, you only make money if people are investing in you for this stuff
@ErikReasonableRatesDarling I just found a new way to break MERGE dbfiddle.uk/kdv1XQYq
2
On my local instance the error is:
Msg 5180, Level 22, State 1, Line 46
Could not open File Control Block (FCB) for invalid file ID 111 in database 'Sandpit'.
Verify the file location.
Execute DBCC CHECKDB.
Msg 596, Level 21, State 1, Line 45
Cannot continue the execution because the session is in the kill state.
Msg 0, Level 20, State 0, Line 45
A severe error occurred on the current command.  The results, if any, should be discarded.
That's hilarious
And now assertions
Location:	 accessor.cpp:521
Expression:	 m_rowsetId == pHoBtAccess->GetRowsetId() && m_dbId == pHoBtAccess->GetDbId() && !anyMetadataChange
Well what did you do then
Location:	 Sql\Ntdbms\storeng\drs\oledb\rowset.h:1269
Expression:	 hRow == reinterpret_cast<HROW>(&m_hRowCurrent)
@ErikReasonableRatesDarling Just a simpler repro dbfiddle.uk/rFyi1sqP
I should submit these dumps to Sean's analysis tool
They might be too many messages for him to read
This is just horribly broken
20:03
Oh that's a cool one. Even simpler repro with a cte instead of a view dbfiddle.uk/cnmZQk3X
The view was the point of normalizing Erik's tables
the NCI makes no difference, it's the same with a clustered PK dbfiddle.uk/pDvTazM4
The point is the QP is convinced there's a row to update because of the MATCH.
There isn't, because of the outer join.
Goodness only knows what it's trying to write to.
@Charlieface None of it is needed, it's just part of the design dbfiddle.uk/z-Bt23Ix
20:26
Works fine as an UPDATE of course
One table repro dbfiddle.uk/Cdyp-wow
It's a shame updates don't turn into inserts in that kind of case
That's the reason I was using MERGE, I wasn't intentionally trying to break it
20:43
Just saw somebody comment on a q about bulk insert on Stack Overflow, he says you should drop your indexes and recreate them afterwards, for "better performance". Well, yeah, but at a cost of rebuilding the indexes, so doesn't make sense unless you're inserting most or all of the whole table.
21:04
did you promptly correct them with a helpful comment
nah some people you can't change ...looks in mirror...
there's always partitioning
21:20
@PaulWhite wow.... that's.... wow
Isn't it
I especially enjoyed hRow == reinterpret_cast<HROW>(&m_hRowCurrent)
21:47
@PaulWhite Speaking of which, the SSL cert expired and I'm 80% done making it a stand alone application, not that you of all people would need it, but just giving an update.
hRow, must be a type of greeting, "oh hrow!"
I didn't know you spoke Chinese
A row? You can't handle a row!
> Effective February 14, 2025, the price for Microsoft 365 Personal subscriptions will increase from USD 69.99* per year to USD 99.99* per year.
But yeah the first one about the FCB is still best. That's low level stuff really going wrong
@ErikReasonableRatesDarling Everything's more expensive on Valentine's Day
Microsoft really do take the piss, don't they
21:53
not very romantic
That's a 43% price rise
Does it last for 522 days instead of 365
oh now i see why
bro you can't just call something included when you charge more money for it
Bro Pilot now included?
Yeah that's pretty low even for marketing people
oy
perhaps i need to make my rates less reasonable
and blame microsoft

« first day (5165 days earlier)