Quantcast
Channel: SAP Adaptive Server Enterprise (SAP ASE) for Custom Applications
Viewing all articles
Browse latest Browse all 151

What is in my statement cache?

$
0
0

I was talking with one of our customers the other day who had a whole bunch of disparate applications attaching to his server.

It really is not unusual from what I have seen to have legacy applications connecting to ASE that the DBA has no knowledge of at all, things just 'work' .

 

Anyway, from what I explained I thought it would be worth detailing here, there are specific formats and properties of the various entries in

the statement cache that help identify the possible setup of the source application. 

We have enhancements in 15.7 and above to allow dynamic sql to be stored in the regular statement cache also (more on that below).

 

The statement cache stores the statement text object, the LWPs (lightweight procedures) themselves reside in the procedure cache.

 

Note that monCachedProcedures\monCachedStatement are the two MDA tables relevant to looking at the individual LWPs.

Here I am making use of of a few things...

 

show_cached_text(<ssql_id>) --ssql_id being the id of the statement in the statement cache (also the lwp procedure id)

dbcc prsqlcache (<ssql_id>, 0||1) --ssql_id is optional but without it will print the whole statement cache. 1 for second parameter will show query plan in showplan format.

show_cached_plan_in_xml(<ssql_id>,0,6) --very useful, this can display for you the compile time bind values as well as execution time bind values.

 

..Ok so I'm not actually using this last one for the purposes of this post, but thought it was definitely worth mentioning, it's a great addition.

 

The ssql_id of a statement can be found in the showplan output or you can actually search monCachedStatement based on the statement text.

The configuration option 'enable stmt cache monitoring' will need to be enable to run the queries against monCachedStatement.

This exposes a number of other fields that we now store with the entry in the statement cache, and in fact are key players

when it comes to identifying the correct statement on subsequent executions, more later..

 

select mp.ObjectName, PlanID,QuotedIdentifier,convert (char(100),show_cached_text(ms.SSQLID)),UseCount,StmtType,SSQLID,Hashkey from monCachedStatement ms, monCachedProcedures mp

where ms.SSQLID=mp.ObjectID

and show_cached_text(ms.SSQLID) like 'select id from sysobjects%' --yeh, I know..not the best query

 

Also, I would strongly suggest not querying the MDA tables like this directly on a production server, make persistent copies of the data and then query those

tables.

 

ObjectName                     PlanID      QuotedIdentifier TransactionIsolationLevel                                                                        UseCount    StmtType SSQLID      Hashkey    

------------------------------ ----------- ---------------- ------------------------- ---------------------------------------------------------------------- ----------- -------- ----------- -----------

*ss0883087873_1444758355ss*    216         0                1                         select id from sysobjects where id =@@@V0_INT(@@@V0_INT INT)           1           1        883087873   1444758355 

*ss0227085536_1999730024ss*    128         0                1                         select id from sysobjects where id=@@@V0_INT(@@@V0_INT INT)            1           1        227085536   1999730024 

*ss0243085593_1258308435ss*    130         0                1                         select id from sysobjects where id= @@@V0_INT(@@@V0_INT INT)           2           1        243085593   1258308435 

*ss0259085650_1191925839ss*    132         0                1                         select id from sysobjects where id = @@@V0_INT(@@@V0_INT INT)          2           1        259085650   1191925839 

*ss0931088044_1309953367ss*    222         0                1                         select id from sysobjects where id = 1                                 1           1        931088044   1309953367 

*ss1075088557_1046503768ss*    240         0                1                         select id from sysobjects where id= @simon(@simon INT output)          1           1        1075088557  1046503768 

*ss1059088500_1394150455ss*    238         1                1                         select id from sysobjects where id= @p0(@p0 INT output)                1           1        1059088500  1394150455 

*ss0067084966_1394158647ss*    104         1                1                         select id from sysobjects where id= @P0(@P0 INT output)                4           1        67084966    1394158647 

*ss0419086220_0208369987ss*    151         1                1                         select id from sysobjects where id= @dr_ta0(@dr_ta0 INT output)        1           1        419086220   208369987  

*ss0467086391_0208369987ss*    161         0                1                         select id from sysobjects where id= @dr_ta0(@dr_ta0 INT output)        2           1        467086391   208369987  

*ss0547086676_1393102647ss*    171         0                1                         select id from sysobjects where id= @s(@s INT output)                  1           1        547086676   1393102647 

*ss1027088386_1394150455ss*    234         1                0                         select id from sysobjects where id= @p0(@p0 INT output)                1           1        1027088386  1394150455

*ss1155088842_1664707590ss*    250         1                1                         select id from sysobjects where id= @0001(@0001 INT output)            1           1        1155088842  1664707590 

 

The next lot will only be seen with 'streamlined dynamic SQL' = 1.

Part of the enhancement under this option was that dynamically prepared statements which previously were only available in a connection

specific dynamic sql cache are now able to be stored in the regular statement cache to be shared across connections.

 

 

*sq0051084909_1393122376ss*    157         1                1                         select id from sysobjects where id= ?                                  19          3        51084909    1393122376 

*sq0051084909_1393122376ss*    197         1                1                         select id from sysobjects where id= ?                                  19          3        51084909    1393122376 

*sq0947088101_1393122376ss*    224         1                0                         select id from sysobjects where id= ?                                  1           3        947088101   1393122376 

*sq0627086961_1393122376ss*    181         0                1                         select id from sysobjects where id= ?                                  3           3        627086961   1393122376 

*sq0083085023_1394150455ss*    106         1                1                         select id from sysobjects where id= @p0                                2           2        83085023    1394150455 

*sq0387086106_0208369987ss*    145         1                1                         select id from sysobjects where id= @dr_ta0                            1           2        387086106   208369987  

*sq0611086904_1393102647ss*    179         0                1                         select id from sysobjects where id= @s                                 1           2        611086904   1393102647 

*sq0867087816_1664707590ss*    214         1                1                         select id from sysobjects where id= @0001                              1           2        867087816   1664707590 

*sq0915087987_1309953367ss*    220         0                1                         select id from sysobjects where id = 1                                 1           2        915087987   1309953367 

 

If your clients are standard ct-lib isql, then the most common form of statement you will see in the statement cache is an entry with the three ampersands

as the prefix of the variable name. These will be generated when 'enable literal parameterization' is enabled at the server-level or literal_autoparam is on at session level. You may also see these generated from any client application issuing statements with fixed (non parameterised at client-side) search arguments.

These statements may be regular or prepared, but crucially, not 'dynamically' prepared - more on that in a bit.

 

 

select id from sysobjects where id =@@@V0_INT(@@@V0_INT INT)

select id from sysobjects where id=@@@V0_INT(@@@V0_INT INT)

select id from sysobjects where id= @@@V0_INT(@@@V0_INT INT)

select id from sysobjects where id = @@@V0_INT(@@@V0_INT INT)

 

All with a name of *ss<ssql_id>_<hashkey>ss*

 

I'm not going to go into literal parameterisation in this post, but to put it simply it takes constant search arguments and replaces them with parameters

to enable the plan to be re-used for a different set of search arguments, in essence to cut down on compilation overhead. Lots of good stuff in this area

that could be discussed but not here and now.

Note the differing white space around the equality operator, having no white space on either side of the operator will form a unique statement, more than

one space though and they'll be stripped and it'll get lumped in with the single space 'versions'. Comments are kept with the statement (and are used in hashkey generation) if they are sent to the ASE.

 

Note these all have a StmtType of 1, these are all language statements or ad-hoc sql (from the perspective of the server), their lwp name will start with *ss

 

select id from sysobjects where id = 1

 

This one is a regular statement but with no literal parameterisation.

 

Next we have what we call parameterised language statements, these can of course be ad-hoc queries using a declared variable (the first example):-

 

 

select id from sysobjects where id= @simon(@simon INT output)

 

 

If you see the following naming convention for the variables, then this is likely jConnect JDBC driver  (jconn3.jar (6.x) or jconn4.jar (7.x)):-

 

 

select id from sysobjects where id= @p0(@p0 INT output)

 

Parameterised language statements may be sent from pretty much any client (ODBC,JDBC, ADO.NET,OLEDB, ct-lib, esql etc) and are likely to either

be prepared statements with unnamed placeholders, i.e. a question mark (but not dynamically prepared), or statements which were intended to be dynamically prepared that the server didn't permit ( ? placed in the wrong place).

 

Upper case P used by jTDS driver:-

 

select id from sysobjects where id= @P0(@P0 INT output)

 

 

Note the number suffix will be incremented dependant on the number of variables in the statement.

 

ODBC driver (native Sybase ODBC)\ OLEDB driver \ ADO.NET driver (for the ADO.NET ? can only be used with NamedParameters=false):-

 

select id from sysobjects where id= @dr_ta0(@dr_ta0 INT output)

 

If you look the the next one in the list, you'll see it has the same hashkey as the one above. The difference here is the quoted_identifiers option.

If this is set it will create a new lwp.

 

Next:-

 

select id from sysobjects where id= @s(@s INT output)

 

This is just a statement using named parameters - AseCommand.Parameters.Add("@s", SqlDbType.Int).Value = 1 for .NET driver for example.

Most APIs have a way of doing this.

 

The next one again appears to be a repeat statement, ah, but hang on it has a different isolation level.

Before 15.7, change of isolation level was a trigger to bump the schemacnt and recompile, but not any more, it will generate a new lwp.

This also applies to optimization goal & optimization level - NOTE this is relevant for statement cache lwps not stored procedures.

 

Next:-

 

select id from sysobjects where id= @0001(@0001 INT output)

 

This is usually the format seen with the older DataDirect odbc drivers (dare I speak of them ).

 

Now we have a few of these:-

 

select id from sysobjects where id= ?

 

The first two are in fact exactly the same, check the lwp name, the two rows represent 2 query plans, i.e. one was in-use so we had to compile

another (or clone on later versions).

These are dynamically prepared statements, ct_dynamic from ct-lib, SQLPrepare from native ODBC, PreparedStatement in JDBC, Ase\Odbc\OleDbcommand.Prepare().

OLEDB\ODBC\jConnect with whatever property determines this for the relevant connection string (DYNAMICPREPARE=1\DYNAMIC_PREPARE=true).

 

 

You will only see question marks as search arguments (with no quotes) from dynamically prepared statements.

They have a StmtType of 3.

They will be sent by the client as create proc statements with an external name to communicate with the server, don't get

confused by the statements you may see in monSysSQLText, these may look something like

 

create proc myquery --custom name set in ct_dynamic call

create proc jtds000001 --jtds

create proc dyn100 --jdbc, possibly other on some versions

create proc A0 - ODBC\OLEDB

 

These are pretty much meaningless from a debug perspective, the internal name of the lwp stored begins with *sq.

See above, all of these are now in the statement cache and that is down to 'streamlined dynamic SQL'. Without

that feature enabled you will not see dynamic prepared statements in the statement cache.

 

Finally, lets look the final five, these begin with *sq but look like regular ad-hoc queries, or rather parameterised language statements.

Note the StmtType=2, this means they are cursors, specifically either straight language based cursor or TDS cursors based on a language

statement. An lwp is created for the cursor, which means it is converted to a procedural cursor (or execute cursor).

This is also a 'streamlined dynamic SQL' enhancement.

I'm in danger of getting too much into the nitty gritty here but feel we need to make the job complete!

You won't see a StmtType=2 for a dynamically prepared statement (that uses cursors) as the cursor is declared on top of the external lwp name, not the

language statement under the external lwp.

Cursors will be used if the correct method for whatever API is called to make it a cursor - or the correct connection parameter is set (UseCursor=1 for ODBC\OLEDB, IMPLICIT_CURSOR_FETCH_SIZE=<X>\SELECT_OPENS_CURSOR=true for jConnect).

 

The very last entry there is a regular ad-hoc declare cursor\open cursor (which will not go through literal parameterisation by the way)

just to show that the *sq lwps are not necessarily restricted to the premise of a prepared statement, although as already demonstrated they *usually* are.

 

If you made it this far hopefully you found something useful you can take away!


Viewing all articles
Browse latest Browse all 151

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>