I am currently building a number of logging and analysis tools to keep tabs on our SQL environment. We are currently using SQL Server 2014.
What I want to do is keep check of all the parameters that are passed to our reports during the day. All of the reports are currently using stored procedures so in my table or a select statement based on a table is output the stored procedure with the parameters for every time the report was run.
At the end of the day I would then like to be able to take the outputted statement and run it in SSMS without having to use the report. I have been looking at the ExceutionLogStorage table and the ExecutionLog view's and though it has most of the information that I need, the parameters are not in an easily usable state.
Has anyone done something similar to what I have described?
You need to add logging part in your original SP, for example:
Alter procedure a
(#parameter)
As
Begin
..
..
Insert into loggingTable(col)
Values(#parameter)
..
..
End
Then query directly against that loggingTable for getting the history of used parameters
A Google search around this topic quickly brought up the following blog post already identified by the OP as useful and shown below (this query itself is actually an expansion of work linked to by LONG's answer below)
SELECT TOP 1 ParValue
FROM (
SELECT els.TimeEnd
, IIF(CHARINDEX('&' + 'ParameterName' + '=', ParsString) = 0, 'ParameterName',
SUBSTRING(ParsString
, StartIndex
, CHARINDEX('&', ParsString, StartIndex) - StartIndex)) AS ParValue
FROM (SELECT ReportID, TimeEnd
, '&' + CONVERT(VARCHAR(MAX), Parameters) + '&' AS ParsString
, CHARINDEX('&' + 'ParameterName' + '=', '&' + CONVERT(VARCHAR(MAX), Parameters) + '&')
+ LEN('&' + 'ParameterName' + '=') AS StartIndex
FROM ExecutionLogStorage
WHERE UserName='UserName' -- e.g. DOMAIN\Joe_Smith
) AS els
INNER JOIN [Catalog] AS c ON c.ItemID = els.ReportID
WHERE c.Name = 'ReportName'
UNION ALL
SELECT CAST('2000-01-01' AS DateTime), 'ParameterName'
) i
ORDER BY TimeEnd DESC;
Both these approaches though really only give us a starting point since they (variously) rely upon us knowing in advance the report name and parameter names. Whilst we can quickly make a couple of changes to Ken Bowman's work to get it to run against all executions of all reports, we still have the problem that the query hardcodes the parameter name.
The parameters required to execute a report are stored on the Catalog table in the Parameter column. Although the column has a datatype ntext, it is actually storing an XML string. Meaning we can use an XPath query to get at the parameter names
with
CatalogData as (
select ItemID, [Path], [Name], cast(Parameter as xml) 'ParameterXml'
from Catalog
where [Type] = 2),
ReportParameters as (
select ItemID, [Path], [Name], ParameterXml, p.value('Name[1]', 'nvarchar(256)') 'ParameterName'
from CatalogData
cross apply ParameterXml.nodes('/Parameters/Parameter') as Parameters(p))
select *
from ReportParameters;
Executing this query will list all reports on the server and their parameters. Now we just need to combine this with Ken Bowman's query. I've gone with a CTE approach
with
CatalogData as (
select ItemID, [Path], [Name], cast(Parameter as xml) 'ParameterXml'
from Catalog
where [Type] = 2),
ReportParameters as (
select ItemID, [Path], [Name], p.value('Name[1]', 'nvarchar(256)') 'ParameterName'
from CatalogData
cross apply ParameterXml.nodes('/Parameters/Parameter') as Parameters(p))
select
els.TimeEnd
, c.[Name]
, rp.ParameterName
, iif(
charindex(
'&' + rp.ParameterName + '=', ParametersString) = 0
, rp.ParameterName, substring(ParametersString
, StartIndex, charindex('&', ParametersString, StartIndex) - StartIndex
)) 'ParameterValue'
from (
select
ReportID
, TimeEnd
, rp.ParameterName
, '&' + convert(varchar(max), Parameters) + '&' 'ParametersString'
, charindex(
'&' + rp.ParameterName + '=',
'&' + convert(varchar(max), Parameters) + '&'
) + len('&' + rp.ParameterName + '=') 'StartIndex'
from
ExecutionLogStorage
inner join ReportParameters rp on rp.ItemID = ReportID) AS els
inner join [Catalog] c on c.ItemID = els.ReportID
inner join ReportParameters rp on rp.ItemID = c.ItemID and rp.ParameterName = els.ParameterName;
Note that the parameter values are passed to the report as part of a URL, so you'll still need get rid the literal space encoding and so on. Also, this doesn't (yet...) work for multi-value parameters.
Related
I've got a report that has 6 parameters. All parameters need to be optional and 3 have to be multi-value. One of the optional parameters is a dropdown, the rest are manually keyed in text boxes.
The Where clause below works when there are multiple #VendorNum values and one #FullJA value, but fails with multiple #FullJA values regardless of the #VendorNum count.
Parameters:
#VendorNum - keyed manually by user (space delimited) - optional, can be multivalue
#FullJA - keyed manually by user (space delimited) - optional, can be multivalue
#BU - optional, can be multivalue - when #JA is populated, this will auto-populate, if #JA isn't populated it's a dropdown with all selected.
#JA3 - keyed by user - optional, single value
#StartDate and #EndDate - optional single values
select * from some_table
WHERE
/*FULL JA*/
(
SUBSTRING(VendorNum, PATINDEX('%[^0]%', VendorNum + '.'), LEN(VendorNum)
) IN (#VendorNum)
AND LEFT(JA, 7) IN (#FullJA)
AND BU IN(#BU)
AND #JA3 IS NULL
)
OR
/*DATE RANGE*/
(
SUBSTRING(VendorNum, PATINDEX('%[^0]%', VendorNum + '.'), LEN(VendorNum)
) IN (#VendorNum)
AND LEN(ISNULL(CONVERT(VARCHAR(20), Cleared_When), '0')) >= #ClearedOnly
AND ad.Audit_Publish_Date >= ISNULL(#StartDate, '2015-01-01')
AND ad.Audit_Publish_Date <= ISNULL(#EndDate, '2025-12-31')
AND BU IN (#BU)
AND #FullJA IS NULL
AND #JA3 IS NULL
)
/*BUS UNIT AND JA3*/
OR (
SUBSTRING(VendorNum, PATINDEX('%[^0]%', VendorNum + '.'), LEN(VendorNum)
) IN (#VendorNum)
AND BU IN (#BU)
AND ad.Audit_Publish_Date >= ISNULL(#StartDate, '2015-01-01')
AND ad.Audit_Publish_Date <= ISNULL(#EndDate, '2025-12-31')
AND LEFT(JA, 3) = (#JA3)
AND #FullJA IS NULL
)
/*BUS UNIT ONLY*/
OR (
SUBSTRING(VendorNum, PATINDEX('%[^0]%', VendorNum + '.'), LEN(VendorNum)
) IN (#VendorNum)
AND BU IN (#BU)
AND ad.Audit_Publish_Date >= ISNULL(#StartDate, '2015-01-01')
AND ad.Audit_Publish_Date <= ISNULL(#EndDate, '2025-12-31')
AND #JA3 IS NULL
AND #FullJA IS NULL
)
The dataset parameter values for #FullJA and #VendorNum are both
=IIF(InStr(Parameters!FullJA.Value," ")>0,SPLIT(Parameters!FullJA.Value," "),Parameters!FullJA.Value) and all params are set as NOT multivalue, with nulls allowed.
Any help would be greatly appreciated. I've written over 200 reports for this project and this is the only one that is really grinding my gears!
Thanks!
I would approach this by building up some temp tables / table variables, to hold the potentially multi-valued variables, and then joining to those tables. This has the advantage of you being able to insert all possible values, in the case they have omitted the variable. So, you'd split your strings and put them into those tables (something along the lines of this example) if given the variable, and otherwise just do an insert into to populate your temp table / table variable.
For a split function, I prefer something like this:
create FUNCTION [dbo].[Split] (#sep VARCHAR(32), #s VARCHAR(MAX))
RETURNS TABLE
AS
RETURN
(
SELECT r.value('.','VARCHAR(MAX)') as Item
FROM (SELECT CONVERT(XML, N'<root><r>' + REPLACE(REPLACE(REPLACE(#s,'& ','& '),'<','<'), #sep, '</r><r>') + '</r></root>') as valxml) x
CROSS APPLY x.valxml.nodes('//root/r') AS RECORDS(r)
)
GO
GRANT SELECT
ON OBJECT::[dbo].[Split] TO PUBLIC
AS [dbo];
I would then put those variables into a table using something like this (my separator is a ", "):
select ltrim(rtrim(ppl.Item)) as PersonName
into #gppl
from dbo.Split(', ', #PersonListForCompare) as ppl
You would do something more like:
select ltrim(rtrim(vnd.Item)) as VendorNum
into #vendorNums
from dbo.Split(', ', #VendorNum) as vnd
You would then join to that temp table just like any other table & use it to limit your results that way. In your case, you want to put in all vendors (possibly) if they didn't give you any input. So, you'd do something like:
create table #vendorNums (VendorName varchar(64)) --I have no idea, here, what this data looks like
if #VendorNum is not null and datalength(#VendorNum) > 0
insert into into #vendorNums (VendorNum)
select ltrim(rtrim(vnd.Item))
from dbo.Split(', ', #VendorNum) as vnd
else
insert into into #vendorNums (VendorNum)
select VendorNum
from dbo.Vendors
That said, I think that you could use your select from dbo.Split directly as a table in a join, rather than putting it into the temp table. Only problem would be you'd have to be sure you had data in there to split, or else you're going to have a bunch of combinations to get the right match-up of null parameters vs. filled ones.
SQL statement:
(select top 1 [egrp_name] from [Enotify Group] where [egrp_id] in (a.grp_id) )
e value of a.grp_id is '0,1145' and i am getting error
Conversion failed when converting the varchar value '0,1145' to data type int.
Can anybody tell me how can i change '0,1145' to 0,1145 in above case, so my query does work and also if their is any other way to do this
You can use a split-string function to change your comma delimited string into a table.
select top(1) [egrp_name]
from [Enotify Group]
where [egrp_id] in (
select Value
from dbo.SplitInts(a.grp_id)
);
One version of a split-string function that you can use if you like:
create function dbo.SplitInts(#Values nvarchar(max)) returns table with schemabinding
as
return
(
select T2.X.value(N'.', N'int') as Value
from (select cast(N'<?X '+replace(#Values, N',', N'?><?X ') + N'?>' as xml).query(N'.')) as T1(X)
cross apply T1.X.nodes(N'/processing-instruction("X")') as T2(X)
);
Or you can use like.
select top(1) [egrp_name]
from [Enotify Group]
where ','+a.grp_id +',' like '%,'+cast(egrp_id as varchar(11))+',%' ;
I have the following mssql query that I found on the net that is supposed to help me with a complex mysql query that I have been struggling with for a few days now.
SELECT
inv.typeID AS typeID,
inv.typeName AS typeName,
invGroups.groupName AS groupName,
inv.published AS published,
inv.description AS description,
rankVal.valueFloat AS rank,
replace (( SELECT skills.attributeName AS [data()]
FROM dgmTypeAttributes tattr -- Link between skillbook and attributes
INNER JOIN dgmAttributeTypes skills ON (skills.attributeID = tattr.valueInt)
WHERE (tattr.typeID = inv.typeID)
AND (tattr.attributeID IN (180, 181)) -- Primary and secondary attributes
ORDER BY inv.typeID FOR xml path('')), ' ', ',') AS prisec,
replace (( SELECT RTRIM(CAST(inv2.typeID AS varchar)) + ',' AS [data()]
FROM (SELECT * FROM dgmTypeAttributes WHERE (attributeID in (182, 183, 184)) -- Pre-req skills 1, 2, and 3
AND (typeID = inv.typeID)) tattr2
INNER JOIN invTypes inv2 ON (tattr2.valueInt = inv2.typeID)
ORDER BY inv.typeID FOR xml path('')), ' ', ' ') AS prereq,
replace (( SELECT RTRIM(CAST(tattr2.valueInt AS varchar)) + ',' AS [data()]
FROM (SELECT * FROM dgmTypeAttributes WHERE (attributeID in (277, 278, 279)) AND (typeID = inv.typeID)) tattr2 -- Link between skillbook and attributes
ORDER BY inv.typeID FOR xml path('')), ' ', ' ') AS prereqlvl
FROM invTypes inv
INNER JOIN invGroups ON (inv.groupID = invGroups.groupID)
INNER JOIN dgmTypeAttributes rankVal ON (inv.typeID = rankVal.typeID)
WHERE invGroups.categoryID = 16 -- Skillbooks category
AND rankVal.attributeID = 275 -- Skill rank attribute
AND inv.published = 1
GROUP BY inv.typeID, inv.typeName, invGroups.groupName, inv.published, inv.description, rankVal.valueFloat
ORDER BY invGroups.groupName, inv.typeName
I am so so with mysql but I know nothing of mssql. Can somebody recommend a good method of converting this query that is low or now cost? I do not expect somebody to convert it for me as that would be asking too much, but some suggestions that would point me in the rite direction (aside from learning mssql lolz) would be very nice. Thank you for your time and patience.
'Recommendation: extract the data out of you MySQL database in a delimited file (csv) using the utf8 (unicode) character set. Import into SQL Server using bcp specifying utf8 with "-Jutf8" parameter and character mode "-c".' See this site. Also, there's a nice tool for this.
Those subqueries with FOR XML PATH('') seem to be used to concatenate strings1. See if you can replace them with GROUP_CONCAT in MySQL. The other bits seem to be standard SQL.
hey guys,
i have a query in sql server which takes atleast 10-15 seconds to execute, and when this is called in asp.net, it is more worst there, it just throws request timeout error.
Below is the query i am using.
SELECT C.Id,
C.Summary,
C.Title,
C.Author,
CONVERT(VARCHAR(12), C.PublishDate, 104)
AS 'DATE',
'/Article/' + SUBSTRING(dbo.RemoveSpecialChars(C.Title), 0, 10) + '/' + CAST(CA.CategoryId AS VARCHAR(MAX)) + '/' + CAST(C.Id AS VARCHAR(MAX)) +
'.aspx' AS
'URL'
FROM CrossArticle_Article C
INNER JOIN CrossArticle_ArticleToCategory CA
ON C.Id = CA.ArticleId
WHERE C.Title LIKE '%' + #KEYWORD + '%'
OR C.Summary LIKE '%' + #KEYWORD + '%'
OR C.Article LIKE '%' + #KEYWORD + '%'
SELECT ##ROWCOUNT
Below are the Fields Specification.
Id int Primary Key
Summary nvarchar(1000)
Title nvarchar(200)
Author nvarchar(200)
PublishDate DateTime
CategoryId int PrimaryKey
i think this can be resolved by using Indexing on these columns using include.. i checked over net, but didnt find any solution..
i would appreciate if i could get help for the same.
Thanks and Regards
Abbas Electricwala
Ordinary column indexing most likely cannot help your query, unfortunately. LIKE conditions can only be assisted by indexes when they are in the form of value% (meaning that you can only have a wildcard on the end of the expression; the prefix must be static).
I am assuming that you already have an index on CrossArticle_Article.Id and CrossArticle_ArticleToCategory.ArticleId. If not, you should add those.
I don't suppose anyone knows whether a SQL Server Agent Job can ask information about itself, such as its own ID, or the path it's running from? I'm aware of xp_sqlagent_enum_jobs and sp_help_job but this doesn't help, because you have to specify the job ID.
The idea is that we want code that we don't have to manage by being able to call a sproc which will identify the current job. Any ideas?
Yes, but it isn't pretty.
Look at the sys.sysprocesses (or dbo.sysprocesses in SQL 2000 and below). The program name will be SQL Agent something with a binary value at the end. That binary value is the binary vale of the guid of the job. So, substring out that value and do a lookup against the msdb.dbo.sysjobs table to find out what job it is (you'll need to cast the sysjobs.job_id to varbinary(100) to get the values to match).
I told you it wasn't pretty, but it will work.
nasty!!! but i think it might work...
eg. used within a job - select * from msdb..sysjobs where job_id = dbo.fn_currentJobId()
let me know.
create function dbo.fn_CurrentJobId()
returns uniqueidentifier
as
begin
declare #jobId uniqueidentifier
select #jobId = j.job_id
from master..sysprocesses s (nolock)
join msdb..sysjobs j (nolock)
on (j.job_id = SUBSTRING(s.program_name,38,2) + SUBSTRING(s.program_name,36,2) + SUBSTRING(s.program_name,34,2) + SUBSTRING(s.program_name,32,2) + '-' + SUBSTRING(s.program_name,42,2) + SUBSTRING(s.program_name,40,2) + '-' + SUBSTRING(s.program_name,46,2) + SUBSTRING(s.program_name,44,2) + '-' + SUBSTRING(s.program_name,48,4) + '-' + SUBSTRING(s.program_name,52,12) )
where s.spid = ##spid
return #jobId
end
go
thanks for the info though