I want to check each spid/session isolation level in SQL Server. My question is specific to READ COMMITTED SNAPSHOT - sql-server-2014

How to check where all my currently running sessions's isolation level. what we set at database level is READ COMMITTED SNAPSHOT. I want to make sure each session is using that to make sure something is not getting changed through application while creating session-connection to SQL Server
I have tried below query but it shows only READ COMMITTED.
SELECT transaction_sequence_num,
commit_sequence_num,
is_snapshot,
t.session_id,
first_snapshot_sequence_num,
max_version_chain_traversed,
elapsed_time_seconds,
host_name,
login_name,
CASE transaction_isolation_level
WHEN '0' THEN
'Unspecified'
WHEN '1' THEN
'ReadUncomitted'
WHEN '2' THEN
'ReadCommitted'
WHEN '3' THEN
'Repeatable'
WHEN '4' THEN
'Serializable'
WHEN '5' THEN
'Snapshot'
END AS transaction_isolation_level
FROM sys.dm_tran_active_snapshot_database_transactions t
JOIN sys.dm_exec_sessions s
ON t.session_id = s.session_id;

Since the query you posted inner joins to dm_tran_active_snapshot_database_transactions only sessions using row versioning will by returned. To return sessions using the Read Commited Snapshot Isolation level (RSCI) and sessions with other isolation levels left join to dm_tran_active_snapshot_database_transactions and check for a non-match (null join column). I wasn't sure if you needed all the columns you listed from dm_tran_active_snapshot_database_transactions so a condensed version is below, and any columns can be added back as needed. The total_elapsed_time in dm_exec_sessions is in milliseconds while the elapsed_time_seconds from dm_tran_active_snapshot_database_transactions is in seconds and this example returns the result in seconds regardless of which DMV the session originates from. I'd also recommend always aliasing columns, especially when joins are involved.
SELECT
S.session_id,
S.host_name,
S.login_name,
--divide milliseconds in total_elapsed_time by 1000
--to get time in seconds if session not using Read Committed SnapShot
COALESCE(T.elapsed_time_seconds, (S.total_elapsed_time / 1000.0)) as TimeElapsedInSeconds,
S.transaction_isolation_level,
CASE WHEN T.session_id is not null then 'ReadCommittedSnapShot' ELSE
CASE S.transaction_isolation_level
WHEN 0 THEN 'Unspecified'
WHEN 1 THEN 'ReadUncomitted'
WHEN 2 THEN 'ReadCommitted'
WHEN 3 THEN 'Repeatable'
WHEN 4 THEN 'Serializable'
WHEN 5 THEN 'Snapshot' END
END as transaction_isolation_level
FROM SYS.DM_EXEC_SESSIONS S
LEFT JOIN SYS.DM_TRAN_ACTIVE_SNAPSHOT_DATABASE_TRANSACTIONS T on S.session_id = T.session_id

I think this may work
SELECT transaction_sequence_num,
commit_sequence_num,
is_snapshot,
t.session_id,
first_snapshot_sequence_num,
max_version_chain_traversed,
elapsed_time_seconds,
host_name,
login_name,
CASE transaction_isolation_level
WHEN '0' THEN
'Unspecified'
WHEN '1' THEN
'ReadUncomitted'
WHEN '2' THEN
CASE
WHEN
(
SELECT [is_read_committed_snapshot_on]
FROM sys.databases
WHERE [database_id] = s.[database_id]
) = 1 THEN
'ReadCommittedSnapShot'
ELSE
'ReadCommitted'
END
WHEN '3' THEN
'Repeatable'
WHEN '4' THEN
'Serializable'
WHEN '5' THEN
'Snapshot'
END AS transaction_isolation_level
FROM sys.dm_tran_active_snapshot_database_transactions t
JOIN sys.dm_exec_sessions s
ON t.session_id = s.session_id;

Related

SELECT JOIN two tables with multible rows in one row

i got a CSV file from the post of swizerland with all addresses. For the usecase we only need about 10% of the includet data. I try now to merge and cut out not needet data.
Swizerland uses 3 different languages for some citys / street-names. The main-table includes always the spoken language from the city. In a second table there is one row for the second, and a second row for the third language.
At the end I want something like this as output:
PID | ZIP_CODE | DE_CITY | FR_CITY | IT_CITY
My query looks like that:
SELECT
NEW_PLZ1.ONRP,
case
when NEW_PLZ1.SPRACHCODE = '1' then NEW_PLZ1.ORTBEZ18
when NEW_PLZ2.SPRACHCODE = '1' then NEW_PLZ2.ORTBEZ18
end as 'DE_CITY',
case
when NEW_PLZ1.SPRACHCODE = '2' then NEW_PLZ1.ORTBEZ18
when NEW_PLZ2.SPRACHCODE = '2' then NEW_PLZ2.ORTBEZ18
end as 'FR_CITY',
case
when NEW_PLZ1.SPRACHCODE = '3' then NEW_PLZ1.ORTBEZ18
when NEW_PLZ2.SPRACHCODE = '3' then NEW_PLZ2.ORTBEZ18
end as 'IT_CITY'
FROM
NEW_PLZ1
LEFT JOIN NEW_PLZ2 ON NEW_PLZ1.ONRP = NEW_PLZ2.ONRP;
ONRP is the primary key and FK in both tables. SPRACHCODE is 1 for german, 2 for french, 3 for italian.
With the query above I get the result I wish - but one language is always a seperate row (because of the join table with 2 rows)
I tried then use GROUP BY ONRP but getting the following error:
[42000][1055] Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'adressen.NEW_PLZ2.SPRACHCODE' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
I found on stackoverflow following:
-disable strict mode:
this worked to execute the query. but the result is not good. (missing data, wrong data)
-add all used fields in GROUP BY
this worked also to execute query. But also with missing data or wrong data.
Atm my creativity is broken to try out how to solve that. I dont know what else keywords i can search here which would help me.
I'm happy for advice to:
resolve my brainbreaker for the group-by and why its not working or how to do the query for wished output in other way.
important: the query will not be in production env. It will be used only to create a new database/table with the needet data.
Thanks a lot for help
Use an Aggregation function
Of course you must test different functions till you get your result
SELECT
NEW_PLZ1.ONRP,
MAX(case
when NEW_PLZ1.SPRACHCODE = '1' then NEW_PLZ1.ORTBEZ18
when NEW_PLZ2.SPRACHCODE = '1' then NEW_PLZ2.ORTBEZ18
end) as 'DE_CITY',
MAX(case
when NEW_PLZ1.SPRACHCODE = '2' then NEW_PLZ1.ORTBEZ18
when NEW_PLZ2.SPRACHCODE = '2' then NEW_PLZ2.ORTBEZ18
end) as 'FR_CITY',
MAX(case
when NEW_PLZ1.SPRACHCODE = '3' then NEW_PLZ1.ORTBEZ18
when NEW_PLZ2.SPRACHCODE = '3' then NEW_PLZ2.ORTBEZ18
end) as 'IT_CITY'
FROM
NEW_PLZ1
LEFT JOIN NEW_PLZ2 ON NEW_PLZ1.ONRP = NEW_PLZ2.ONRP
GROUP BY NEW_PLZ1.ONRP;

Data base One To Many Relationship Query

I have 3 tables in my DB; Transactions, transaction_details, and accounts - basically as below.
transactions :
id
details
by_user
created_at
trans_details :
id
trans_id (foreign key)
account_id
account_type (Enum -[c,d])
amount
Accounts :
id
sub_name
In each transaction each account may be creditor or debtor. What I'm trying to get is an account statement (ex : bank account movements) so I need to query each movement when the account is type = c (creditor) or the account type is = d (debtor)
trans_id, amount, created_at, creditor_account, debtor_account
Update : I tried the following query but i get the debtor column values all Null!
SELECT transactions.created_at,trans_details.amount,(case WHEN trans_details.type = 'c' THEN sub_account.sub_name END) as creditor,
(case WHEN trans_details.type = 'd' THEN sub_account.sub_name END) as debtor from transactions
JOIN trans_details on transactions.id = trans_details.trans_id
JOIN sub_account on trans_details.account_id = sub_account.id
GROUP by transactions.id
After the help of #Jalos I had to convert the query to Laravel which also toke me 2 more hours to convert and get the correct result :) below is the Laravel code in case some one needs to perform such query
I also added between 2 dates functionality
public function accountStatement($from_date,$to_date)
{
$statemnt = DB::table('transactions')
->Join('trans_details as credit_d',function($join) {
$join->on('credit_d.trans_id','=','transactions.id');
$join->where('credit_d.type','c');
})
->Join('sub_account as credit_a','credit_a.id','=','credit_d.account_id')
->Join('trans_details as debt_d',function($join) {
$join->on('debt_d.trans_id','=','transactions.id');
$join->where('debt_d.type','d');
})
->Join('sub_account as debt_a','debt_a.id','=','debt_d.account_id')
->whereBetween('transactions.created_at',[$from_date,$to_date])
->select('transactions.id','credit_d.amount','transactions.created_at','credit_a.sub_name as creditor','debt_a.sub_name as debtor')
->get();
return response()->json(['status_code'=>2000,'data'=>$statemnt , 'message'=>''],200);
}
Your transactions table denotes transaction records, while your accounts table denotes account records. Your trans_details table denotes links between transactions and accounts. So, since in a transaction there is a creditor and a debtor, I assume that trans_details has exactly two records for each transaction:
select transactions.id, creditor_details.amount, transactions.created_at, creditor.sub_name, debtor.sub_name
from transactions
join trans_details creditor_details
on transactions.id = creditor_details.trans_id and creditor_details.account_type = 'c'
join accounts creditor
on creditor_details.account_id = creditor.id
join trans_details debtor_details
on transactions.id = debtor_details.trans_id and debtor_details.account_type = 'd'
join accounts debtor
on debtor_details.account_id = debtor.id;
EDIT
As promised, I am looking into the query you have written. It looks like this:
SELECT transactions.id,trans_details.amount,(case WHEN trans_details.type = 'c' THEN account.name END) as creditor,
(case WHEN trans_details.type = 'd' THEN account.name END) as debtor from transactions
JOIN trans_details on transactions.id = trans_details.trans_id
JOIN account on trans_details.account_id = account.id
GROUP by transactions.id
and it is almost correct. The problem is that due to the group-by MySQL can only show a single value for each record for creditor and debtor. However, we know that there are exactly two values for both: there is a null value for creditor when you match with debtor and a proper creditor value when you match with creditor. The case for debtor is similar. My expectation for this query would have been that MySQL would throw an error because you did not group by these computed case-when fields, yet, there are several values, but it seems MySQL can surprise me after so many years :)
From the result we see that MySQL probably found the first value and used that both for creditor and debtor. Since it met with a creditor match as a first match, it had a proper creditor value and a null debtor value. However, if you write bullet-proof code, you will never meet these strange behavior. In our case, doing some minimalistic improvements on your code transforms it into a bullet-proof version of it and provides correct results:
SELECT transactions.id,trans_details.amount,max((case WHEN trans_details.type = 'c' THEN account.name END)) as creditor,
max((case WHEN trans_details.type = 'd' THEN account.name END)) as debtor from transactions
JOIN trans_details on transactions.id = trans_details.trans_id
JOIN account on trans_details.account_id = account.id
group by transactions.id
Note, that the only change I did with your code is to wrap a max() function call around the case-when definitions, so we avoid the null values, so your approach was VERY close to a bullet-proof solution.
Fiddle: http://sqlfiddle.com/#!9/d468dc/10/0
However, even though your thought process was theoretically correct (theoretically there is no difference between theory and practice, but in practice they are usually different) and some slight changes are transforming it into a well-working code, I still prefer my query, because it avoids group by clauses, which can be useful, if necessary, but here it's unnecessary to do group by, which is probably better in terms of performance, memory usage, it's easier to read and keeps more options open for you for your future customisations. Yet, your try was very close to a solution.
As about my query, the trick I used was to do several joins with the same tables, aliasing them and from that point differentiating them as if they were different tables. This is a very useful trick that you will need a lot in the future.

Odd behavior with user defined variables in MySQL

I am building a report and using user defined variables to keep the query as compact as possible. What I am noticing in MySQL Workbench and in PHP is that they don't always work properly.
For example:
SELECT
#NewSales := SUM(CASE WHEN `v`.`new_used`='N' THEN 1 ELSE 0 END) AS `NewSales`,
#UsedSales := SUM(CASE WHEN `v`.`new_used`='U' THEN 1 ELSE 0 END) AS `UsedSales`,
#UsedSales + #NewSales AS `TotalSales`
FROM `fi_sales` `s`
LEFT JOIN `vehicles` `v` ON `v`.`stock_number`=`s`.`stock_number`
If I run the above query in Workbench, the first run outputs TotalSales=NULL:
NewSales, UsedSales, TotalSales
3418, 2889, NULL
If I refresh the query, the output produces expected result for TotalSales:
NewSales, UsedSales, TotalSales
3418, 2889, 6307.000000000000000000000000000000
A bit strange; almost as if the variable is not usable in the same query that sets it. I usually work around it by reproducing the calculation without using variables.
My next problem is that if I copy the same query from Workbench into my application (PHP) the TotalSales output will produce "0" zero.
I am sure there is a perfectly good explanation what is going on here but I am having trouble finding it. Any answers are greatly appreciated.
You're in non-deterministic territory using user-defined variables in a query that changes them, and the explanation is straightforward: the answer you get is actually from the previous run of the same query.
UDVs are scoped to your individual database connection. Their values persist between queries, but not across connections. This query is giving you the value of #UsedSales + #NewSales from before the query is run, not after. (Why? Because it just is. There is not a reason... it could go either way. See below.)
SET #UsedSales = 1, #NewSales = 2; and run your query again. Total will be 3 in the next run, clearly the wrong answer (compared to what you expected) but not wrong in the sense that the server is free to resolve these in any order it likes, because they look like constants.
As a general rule, other than in SET statements, you should never assign a value to a user variable and read the value within the same statement.
...
For other statements, such as SELECT, you might get the results you expect, but this is not guaranteed.
...
The order of evaluation for expressions involving user variables is undefined.
https://dev.mysql.com/doc/refman/5.7/en/user-variables.html
You're trying to solve a problem that isn't really a problem.
Instead, either do this:
SELECT
SUM(CASE WHEN `v`.`new_used`='N' THEN 1 ELSE 0 END) AS `NewSales`,
SUM(CASE WHEN `v`.`new_used`='U' THEN 1 ELSE 0 END) AS `UsedSales`,
SUM(CASE WHEN `v`.`new_used` IN ('N','U') THEN 1 ELSE 0 END) AS `TotalSales`
FROM `fi_sales` `s`
LEFT JOIN `vehicles` `v` ON `v`.`stock_number`=`s`.`stock_number`;
Or if you insist, make a derived table (here, named x) and do the extra addition of the resulting columns.
SELECT
x.NewSales,
x.UsedSales,
x.NewSales + x.UsedSales AS TotalSales
FROM (
SELECT
SUM(CASE WHEN `v`.`new_used`='N' THEN 1 ELSE 0 END) AS `NewSales`,
SUM(CASE WHEN `v`.`new_used`='U' THEN 1 ELSE 0 END) AS `UsedSales`
FROM `fi_sales` `s`
LEFT JOIN `vehicles` `v` ON `v`.`stock_number`=`s`.`stock_number`
) x;
This materializes the inner result into a temporary table that is discarded as soon as the query finishes executing.
Or, if you really want clever and short, go with this:
SELECT
COUNT(`v`.`new_used`='N' OR NULL) AS `NewSales`,
COUNT(`v`.`new_used`='U' OR NULL) AS `UsedSales`,
COUNT(`v`.`new_used` IN ('N','U') OR NULL) AS `TotalSales`
FROM `fi_sales` `s`
LEFT JOIN `vehicles` `v` ON `v`.`stock_number`=`s`.`stock_number`;
This works, because COUNT() counts only rows with non-null arguments, and any expression expr OR NULL coerces expr to be evaluated as a boolean expression, and thus is logically equivalent to CASE WHEN expr [IS TRUE] THEN 1 ELSE NULL END and thus can only evaluate to 1 (if expr is truthy)... or NULL (if expr is either false or null)... which an exact match for how COUNT() works.

mysql Query performance is low

I have a query which is running for around 2 hours in last few days. But
before that it took only 2 to 3 minutes of time. i could not able to find
the reason for its sudden slowness. Can any one help me on this?
Please find the below query explain plan[![enter image description here][1]]
[1]...
select
IFNULL(EMAIL,'') as EMAIL,
IFNULL(SITE_CD,'') as SITE_CD,
IFNULL(OPT_TYPE_CD,'') as OPT_TYPE_CD,
IFNULL(OPT_IN_IND,'') as OPT_IN_IND,
IFNULL(EVENT_TSP,'') as EVENT_TSP,
IFNULL(APPLICATION,'') as APPLICATION
from (
SELECT newsletter_entry.email email,
newsletter.site_cd site_cd,
REPLACE (newsletter.TYPE, 'OPTIN_','') opt_type_cd,
CASE
WHEN newsletter_event_temp.post_status = 'SUBSCRIBED' THEN 'Y'
WHEN newsletter_event_temp.post_status = 'UNSUBSCRIBED' THEN
'N'
ELSE ''
END
opt_in_ind,
newsletter_event_temp.event_date event_tsp,
entry_context.application application
FROM amg_toolkit.newsletter_entry,
amg_toolkit.newsletter,
(select NEWSLETTER_EVENT.* from amg_toolkit.NEWSLETTER_EVENT,
amg_toolkit.entry_context where newsletter_event.EVENT_DATE >= '2017-07-11
00:01:23' AND newsletter_event.EVENT_DATE < '2017-07-11 01:01:23' and
newsletter_event.ENTRY_CONTEXT_ID = entry_context.ENTRY_CONTEXT_ID and
entry_context.APPLICATION != 'feedbackloop') newsletter_event_temp,
amg_toolkit.entry_context
WHERE newsletter_entry.newsletter_id = newsletter.newsletter_id
AND newsletter_entry.newsletter_entry_id =
newsletter_event_temp.newsletter_entry_id
AND newsletter.TYPE IN ('OPTIN_PRIM', 'OPTIN_THRD', 'OPTIN_WRLS')
AND newsletter_event_temp.entry_context_id NOT IN
(select d.ENTRY_CONTEXT_ID from amg_toolkit.sweepstake a,
amg_toolkit.sweepstake_entry b, amg_toolkit.user_entry c,
amg_toolkit.entry_context d where a.exclude_data = 'Y' and
a.sweepstake_id=b.sweepstake_id and b.USER_ENTRY_ID=c.USER_ENTRY_ID and
c.ENTRY_CONTEXT_ID = d.ENTRY_CONTEXT_ID)
AND newsletter_event_temp.entry_context_id =
entry_context.entry_context_id
AND newsletter_event_temp.event_date >= '2017-07-11 00:01:23'
AND newsletter_event_temp.event_date < '2017-07-11 01:01:23') a;`
[1]: https://i.stack.imgur.com/cgsS1.png
dont use .*
select only the columns of data you are using in your query.
Avoid nested sub selects if you dont need them.
I don't see a need for them in this query. You query the data 3 times this way instead of just once.
Slowness can be explained by an inefficient query haveing to deal with tables that have a growing number of records.
"Not in" is resource intensive. Can you do that in a better way avoiding "not in" logic?
JOINs are usually faster than subqueries. NOT IN ( SELECT ... ) can usually be turned into LEFT JOIN ... WHERE id IS NULL.
What is the a in a.exclude_data? Looks like a syntax error.
These indexes are likely to help:
newsletter_event: INDEX(ENTRY_CONTEXT_ID, EVENT_DATE) -- in this order
You also need it for newsetter_event_temp, but since that is not possible, something has to give. What version of MySQL are you running? Perhaps you could actually CREATE TEMPORARY TABLE and ADD INDEX.

MySQL Different results from same query/data

I have two servers running MySQL. Both are on windows. One, is my local machime (Windows 7, MySQL 5.6.25, 32bit) and the other is my production webserver (Windows 2012, MySQL 5.7.11-log, 64bit (that's what show variables showed me).
The data is identical between the two. I backed the data up from the windows 7 (using MySQL Workbench) and restored it on the 2012 machine.
I am running a query on both machine but I am getting different results. I have two tables, projects and projectsnotes with a 1:m relationship between them related on projects.id to projectsnotes.idProject. Each note is marked with a date (dComment). The goal of the query is to retrieve project information and the latest comment only.
Here's the query:
select space(1) as cAction,
p.id,
p.iNum,
p.cStatus,
p.cName,
p.cDesc,
ifnull(pl.cNickName, 'UNASSIGNED') as cProjectLeader,
IFNULL(concat (
date_format(pn.dComment, '%Y-%m-%d'),
': ',
pn.cComment
), '') as cComment,
date_format(p.dRequested, '%Y-%m-%d') as dRequested,
date_format(p.dRequired, '%Y-%m-%d') as dRequired,
format(p.nPercentComplete, 2) as nPercentComplete,
p.tLastUpdated,
p.bCompleted,
p.idProjectLeader
from projects p
left outer join projectleaders pl on p.idProjectLeader = pl.id
left outer join (
select idProject,
dComment,
cComment
from projectnotes
order by dComment desc,
tLastUpdated desc
) pn on p.id = pn.idProject
where p.cInstallCode = 'ITM'
and cStatus in ('Pending', 'Active', 'On Hold', 'Completed', 'Cancelled')
and bCompleted = 0
group by iNum
order by iNum;
Now, here's the weird part. When I run this on my Windows 7 machine, I get the right value for cComment. Specifically:
2017-03-28: Text from note replace
That is the latest note. When I run it on the 2012 server:
2016-05-17: Text from this note replaced too
If I run the subquery alone on the 2012 server, I get the right values (namely, a list of all the notes in the reverse order.
Oh, and this note is neither the first nor the last in the notes for this project.
So I am honestly wondering what is going on. Any thoughts on this would be greatly appreciated.
Thanks in advance.
This is expected behavior.
select ...
from projects p
left outer join projectleaders pl on p.idProjectLeader = pl.id
left outer join (...) pn on p.id = pn.idProject
where ...
group by iNum
order by iNum;
Due to MySQL's peculiar handling of GROUP BY, it will not report an error on this query. However, you must keep in mind that, since you use no aggregates, and the GROUP BY will eliminate lots of rows, the rows that are kept in the final result set are determined by rather obscure criteria...
For example:
SELECT a,b FROM t GROUP BY a
Which b will be returned? In some MySQL versions, this will be the first value of b that is found in table t. If table t is ordered in a certain way, this can be exploited. But I would definitely not trust that behavior to stay unchanged between versions... Also, remember MySQL is free to change your join order...
OK. I think I have a solution to this. Instead of doing it with a join I wrote a function that returned the value I needed as follows:
DROP FUNCTION if exists f_lastprojectnote;
DELIMITER $$
CREATE FUNCTION f_lastprojectnote(tidProject varchar(36))
RETURNS varchar(1000) DETERMINISTIC
BEGIN
DECLARE cRetVal VARCHAR(1000);
SELECT concat(date_format(pn.dComment, '%Y-%m-%d'), ': ', pn.cComment) INTO cRetVal
FROM projectnotes pn
WHERE idProject = tidProject
ORDER BY dComment DESC, tLastUpdated DESC
LIMIT 1;
RETURN cRetVal;
END$$
DELIMITER ;
It works...