Tracking report usage - reporting-services

Is there an easy way to track who is running a given report in SSRS 2005, and at what time they are running that report? We have about 80 reports in our SSRS implementation, and are trying to see if there's any that we can safely put out to pasture. If we could easily see somehow which reports aren't being used, that would help us. Any ideas?

There is some good advice and queries for generating reports on this in the following article.
For example, if you want to see the most used reports, you can do the following:
SELECT COUNT(Name) AS ExecutionCount,
Name,
SUM(TimeDataRetrieval) AS TimeDataRetrievalSum,
SUM(TimeProcessing) AS TimeProcessingSum,
SUM(TimeRendering) AS TimeRenderingSum,
SUM(ByteCount) AS ByteCountSum,
SUM([RowCount]) AS RowCountSum
FROM (SELECT TimeStart,
Catalog.Type,
Catalog.Name,
TimeDataRetrieval,
TimeProcessing,
TimeRendering,
ByteCount,
[RowCount]
FROM Catalog
INNER JOIN
ExecutionLog
ON Catalog.ItemID = ExecutionLog.ReportID
WHERE Type = 2
) AS RE
GROUP BY Name
ORDER BY COUNT(Name) DESC,
Name;
One thing to note is that by default the execution log will only keep 2 months worth of data. You can control this behaviour with the ExecutionLogDaysKept server property, see this technet article.

I know this question is so old it has whiskers, but the code below will list each report once with the last time it was run. I highly recommend you create a new folder called "obsolete reports" and move old reports there rather than delete them. That will remove the clutter but still keep them available in case the Accounting Department comes after you for that report they obviously need to run once every 3.26 years.
WITH RankedReports
AS
(SELECT ReportID,
TimeStart,
UserName,
RANK() OVER (PARTITION BY ReportID ORDER BY TimeStart DESC) AS iRank
FROM dbo.ExecutionLog t1
JOIN
dbo.Catalog t2
ON t1.ReportID = t2.ItemID
)
SELECT t2.Name AS ReportName,
t1.TimeStart,
t1.UserName,
t2.Path,
t1.ReportID
FROM RankedReports t1
JOIN
dbo.Catalog t2
ON t1.ReportID = t2.ItemID
WHERE t1.iRank = 1
ORDER BY t1.TimeStart;

i always found the report logs are a bit hard to use. Reporting services keeps a record of all its activity in a table in the reporting database called ExecutionLog
I have a couple of reports i use that query this table, so you can find out what reports are actually used, and who the heaviest users are

You can monitor the report usage using execution logs. Please check this http://technet.microsoft.com/en-us/library/aa964131(SQL.90).aspx
You can also run a query to find report usage. Check Maz's reply in this link http://www.sqlservercentral.com/Forums/Topic433562-150-1.aspx
cheers

This SQL will also give you the data source, user and the request type:
select row_number() over (order by LogEntryId) as Id, LogEntryId,
r.Name AS Report_Name, r.Path AS Report_Path, c2.Name AS Data_Source,
replace(c2.ConnectString,';Unicode=True','') as ConnectString,
SUBSTRING(r.Path, 2, LEN(r.Path) - LEN(r.Name) - 2) AS Folder_Path,
ex.UserName, ex.Format, ex.TimeProcessing, ex.TimeRendering, ex.[RowCount],
CAST (ex.TimeStart as date) AS TimeStart,
DATEPART (hour, ex.TimeStart) AS StartHour,
DATEPART (minute, ex.TimeStart) AS StartMinute,
case
when ex.RequestType = 0 then 'Interactive'
when ex.RequestType = 1 then 'Subscription'
when ex.RequestType = 2 then 'Refresh Cache'
else 'Unknown' end RequestType,
u.UserName as CreatedBy,
ex.Status
from ExecutionLogStorage ex (nolock) --exec log
join Catalog (nolock) r on ex.ReportID = r.ItemID and r.Type = 2 --report
join DataSource ds with (nolock) ON ds.ItemID = r.ItemID --report to connection link
join (select ItemID, Name, SUBSTRING(Content, CHARINDEX('<ConnectString>',Content) + 15, CHARINDEX('</ConnectString>',Content) - CHARINDEX('<ConnectString>',Content) - 15) AS ConnectString
from ( select ItemID, Name, CONVERT(NVARCHAR(MAX),CONVERT(XML,CONVERT(VARBINARY(MAX),Content))) As Content
from Catalog with (nolock) where Type = 5) x
) c2 ON ds.Link = c2.ItemID -- connection
left join Users u on u.UserID = r.CreatedByID

USE ReportServer
SELECT c.Name AS ItemName
, CASE c.Type
WHEN 1 THEN 'Folder'
WHEN 2 THEN 'Report'
WHEN 3 THEN 'Resource'
WHEN 4 THEN 'Linked Report'
WHEN 5 THEN 'Data Source'
ELSE CAST(c.Type AS VARCHAR(100))
END AS ItemType
, c.Path AS ItemPath
, ( SELECT TOP 1 TimeStart FROM dbo.ExecutionLog t1 WHERE t1.ReportID = c.ItemID ORDER BY TimeStart DESC ) AS LastRunDate
, ( SELECT TOP 1 UserName FROM dbo.ExecutionLog t1 WHERE t1.ReportID = c.ItemID ORDER BY TimeStart DESC ) AS LastUser
FROM Catalog AS c WITH (NOLOCK)
WHERE 1=1
--AND c.Type IN (1,2)
--uncomment if searching for reports and folders only

Related

How can I correct this query that involves a CASE statement for a summary?

I'm currently trying to solve an issue revolving around summarizing a list of publishers, their total revenue, total payouts, and their net profit. What makes this slightly complicated is that the total payout is contingent on a case statement (due to having to choose between the higher value of royalties). This case statement was perfectly fine and executed in a previous query that you can see on the SQLFiddle link down below. My issue is that I have a near finished query that addresses what I need but I don't know what correction to make for it to complete. Help would be super appreciated! And if you get it to work, you would be a legit lifesaver!!
Select name,
SUM(book.msrp) AS 'Total Revenue',
SUM(EarningByBook) AS 'Total Payouts',
SUM(book.msrp)-SUM(EarningByBook) AS 'Net Profit'
FROM
(SELECT publisher.name, book.msrp,
(SELECT
CASE WHEN preferred_royalties > standard_royalties
THEN preferred_royalties*copies_sold
ELSE standard_royalties*copies_sold END
AS 'EarningByBook',
copies_sold ,
YEAR(CURDATE())-YEAR(date_published) Years
INNER JOIN book ON publisher.id = book.publisher_id)
FROM author A
JOIN book B ON A.id=B.author_id
JOIN publisher P ON B.publisher_id=P.id)
From publisher
INNER JOIN book ON publisher.id = book.publisher_id) Z
GROUP BY
name;
The SQL fiddle is as follows :
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=b0015a0a4286f9b2c064bbd65525faa5&hide=13312
The output expected should look
Publisher
Total Revenue
Total Payouts
Net Profit
name
20000
1500
18500
name
15000
1000
14000
Consider flattening all your inner selects to a single SELECT subquery.
SELECT sub.publisher
, SUM(sub.msrp) AS 'Total Revenue'
, SUM(sub.EarningByBook) AS 'Total Payouts'
, SUM(sub.msrp) - SUM(sub.EarningByBook) AS 'Net Profit'
FROM
(SELECT
P.`name` AS publisher
, CASE
WHEN A.preferred_royalties > P.standard_royalties
THEN A.preferred_royalties * B.copies_sold
ELSE P.standard_royalties * B.copies_sold
END AS EarningByBook
, YEAR(CURDATE()) - YEAR(B.date_published) AS Years
, B.msrp
, B.copies_sold
FROM author A
INNER JOIN book B
ON A.id = B.author_id
INNER JOIN publisher P
ON B.publisher_id = P.id
) AS sub
GROUP BY
sub.publisher;

How to export user data from two different tables using the backoffice sql manager of Prestashop?

I'm not good with sql so i need help exporting my customer's first names, email address, their country and if possible last times they had access to the store.
I managed to get email and first name by using this query :
SELECT firstname, lastname, email, active FROM psdb_customer
From what i understood, since the other data is stored in a different db table, i should use a join to get data from both tables but i haven't been able to figure out how
Any help is welcome
In the past, I helped someone with something similar in the PrestaShop forum. This query should work, just remember that to get a country of a customer they should have at least one address registered and of course I'm using the default db prefix:
SELECT a.`firstname`,
a.`lastname`,
a.`email`,
(SELECT c.`date_add`
FROM `ps_guest` g
LEFT JOIN `ps_connections` c ON c.`id_guest` = g.`id_guest`
WHERE g.`id_customer` = a.`id_customer`
ORDER BY c.`date_add` DESC LIMIT 1) AS `last_activity`,
(SELECT cl.`name`
FROM `ps_address` ad
LEFT JOIN `ps_country_lang` cl ON cl.`id_country` = ad.`id_country`
WHERE ad.`id_customer` = a.`id_customer`
ORDER BY ad.`id_address` DESC LIMIT 1) AS `country_name`
FROM `ps_customer` a
Rolige's answer is what i was looking for.
Here is another query that allows filtering the results by country (using the id_country)
SELECT SQL_CALC_FOUND_ROWS a.`id_address`,
a.firstname as firstname,
a.lastname as lastname,
cl.id_country as country, cl.`name` as country
FROM `ps_address` a
LEFT JOIN `ps_country_lang` cl ON (cl.`id_country` = a.`id_country`
AND cl.`id_lang` = 1)
LEFT JOIN `ps_customer` c ON a.id_customer = c.id_customer
WHERE 1 AND a.id_customer != 0
AND c.id_shop IN (1)
AND a.`deleted` = 0
AND cl.`id_country` = '8'
ORDER BY a.`id_address` ASC

Count distinct using case when

Let me first start by saying I am new to mysql. I am transitioning from dumping data into excel and using voodoo from there so this new language can be perplexing but I am getting the hang of simple queries.
My first problem that I cannot find an answer for is that I am looking for billable medical encounters and then comparing them to how many patients were seen for that time period. (Edit: It is the "CASE WHEN statement that I am having issue with here)
Billable encounters are counted by unique encounters that can have one of any number of billable codes for that encounter to count as billable. (There are many more codes but I truncated for space)
This is what I have this far which of course is not working:
SELECT
CONCAT(`users`.`ulname`,', ',`users`.`ufname`) AS Provider
, YEAR(e.`date`) AS 'YEAR'
, LEFT(MONTHNAME(e.date), 3) AS 'MONTH'
, COUNT(DISTINCT e.`patientID`) AS Pts
, COUNT(DISTINCT e.`encounterID` CASE WHEN d.`value`
IN ('99213','99214','99212','99203','99202','90832','99393') THEN END AS
Bill_Encs
, COUNT(e.`status`) AS 'Vts_CHK'
, COUNT(e.`status`) - COUNT(DISTINCT e.`encounterID`)
AS No_Codes
FROM enc e
INNER JOIN `mobiledoc`.`users` ON `users`.`uid` = e.`ResourceId`
INNER JOIN `billingdata` billing ON billing.`EncounterId` =
e.`encounterID`
INNER JOIN items i ON i.`itemID` = billing.`itemID`
INNER JOIN `itemdetail` d ON d.`itemID` = i.`itemID`
WHERE e.`date` BETWEEN CAST('2017-10-26' AS DATE) AND CAST('2017-10-31' AS
DATE)
AND d.`propID` = 13
AND billing.`deleteFlag` = 0
AND i.`deleteFlag` = 0
AND e.status = "CHK"
AND e.`deleteFlag` = 0
AND e.`encType` = 1
AND e.`ClaimReq` = 1
AND `users`.`UserType` = 1
GROUP BY Provider ASC, YEAR(e.`date`) ,MONTH(e.`date`) ASC;
If all else is equal (working) I think you just want conditional aggregation.
, COUNT(DISTINCT e.`encounterID` CASE WHEN d.`value`
IN ('99213','99214','99212','99203','99202','90832','99393') THEN END AS
Bill_Encs
becomes
, COUNT(DISTINCT CASE WHEN d.`value`
IN ('99213','99214','99212','99203','99202','90832','99393') THEN e.`encounterID` END AS Bill_Encs

Mixed scenarios in MS Access

This is my table [Property]:
Loanno Balance amount PropertyType
1001045 308731.770000 1
1001045 2007700.740000 2
1001045 3087318905.770 3
1001045 308731.770000 4
1001046 306589.67 1
1001046 456321.23 1
1001046 6932542.89 1
1001047 582563.56 1
1001047 965421.34 2
1001048 567894.34 1
1001048 567894.34 2
I have to get the property type for a [Loanno] having highest balance amount.
If there is a tie in the highest balance amount and if the Property type for the loannumber is different then for that Loan number I have to populate property type as '8'.
So my final Output should look like this:
Loanno PropertyType
1001045 3
1001046 1
1001047 2
1001048 8
Edit
This is what I tried, but I am getting duplicate records.
SELECT DISTINCT
LT.LOANNO,
IIF(COUNTS.MAX_BALANCE > 1, 8,LT.PROPERTY_TYPE) AS PROPERTY_TYPE1
FROM
PROPERTY LT
INNER JOIN
(
SELECT
DISTINCT_ROWS.LOANNO,
COUNT(DISTINCT_ROWS.MaxBalance) AS MAX_BALANCE
FROM
(
SELECT DISTINCT
L.LOANNO,
MaxBalance
FROM
PROPERTY AS L
INNER JOIN
(
SELECT
LOANNO,
MAX(BALANCE_AMOUNT) AS MaxBalance
FROM PROPERTY
GROUP BY LOANNO
) AS SUB
ON (L.LOANNO=SUB.LOANNO)
AND (L.BALANCE_AMOUNT=SUB.MaxBalance)
) AS DISTINCT_ROWS
GROUP BY DISTINCT_ROWS.LOANNO
) AS COUNTS
ON LT.LOANNO=COUNTS.LOANNO
GROUP BY LT.LOANNO, IIF(COUNTS.MAX_BALANCE > 1, 8, LT.PROPERTY_TYPE)
Your query requirements are challenging for Access SQL. A custom VBA function would allow you to use a simpler SELECT statement, but brings issues which may be unacceptable for you:
A UDF (user-defined function) can only be used in a query run within an Access session ... not when you use other code (.Net, Java, PHP, VBScript, etc) to connect to the db and run your query.
UDFs can be slow.
If you can use a UDF, this query using the GetPropertyType function (see below) returns what you asked for. Note I used tblProperties as the table name because Property is a reserved word. Also I assumed Long for the data type of Loanno, Currency for Balance_amount, and Long for Property_Type.
SELECT
sub.Loanno,
GetPropertyType(sub.Loanno,sub.MaxBalance) AS PropertyType
FROM
(
SELECT
Loanno,
Max(Balance_amount) AS MaxBalance
FROM tblProperties
GROUP BY Loanno
) AS sub
ORDER BY sub.Loanno;
This is the function, which I tested with Access 2007.
Public Function GetPropertyType(ByVal pLoanno As Long, _
ByVal pBalance_amount As Currency) As Long
Const cstrQdf As String = "qryLoanPropertyTypesCount"
Dim db As DAO.database
Dim qdf As DAO.QueryDef
Dim lngReturn As Long
Set db = CurrentDb
Set qdf = db.QueryDefs(cstrQdf)
qdf.Parameters("which_Loanno") = pLoanno
qdf.Parameters("which_Balance_amount") = pBalance_amount
If qdf.OpenRecordset()(0) > 1 Then
lngReturn = 8
Else
lngReturn = DLookup("Property_Type", "tblProperties", _
"Loanno=" & pLoanno & " AND Balance_amount=" & _
pBalance_amount)
End If
Set qdf = Nothing
Set db = Nothing
GetPropertyType = lngReturn
End Function
The function uses this saved parameter query, qryLoanPropertyTypesCount:
PARAMETERS which_Loanno Long, which_Balance_amount Currency;
SELECT Count(*) AS num_PropertyTypes
FROM
(
SELECT DISTINCT
p.Loanno,
p.Balance_amount,
p.Property_Type
FROM tblProperties AS p
WHERE
p.Loanno = [which_Loanno]
AND p.Balance_amount = [which_Balance_amount]
) AS sub;
Here's how I built up the query:
Step 1: Create a query to find the maximum balance for each [Loanno] and save that query as [Loanno_MaxBalance]:
SELECT
Loanno,
MAX([Balance amount]) AS MaxBalance
FROM [Property]
GROUP BY Loanno
Step 2a: Create a query to count the number of rows that have the maximum balance, using our saved query above to keep things simple:
SELECT
[Property].Loanno,
[Property].[Balance amount],
COUNT(*) AS RowCount
FROM
[Property]
INNER JOIN
[Loanno_MaxBalance]
ON Loanno_MaxBalance.Loanno=[Property].Loanno
AND Loanno_MaxBalance.MaxBalance=[Property].[Balance amount]
GROUP BY [Property].Loanno, [Property].[Balance amount]
Step 2b: That doesn't look too scary, so let's incorporate the SQL from Step 1 as a subquery:
SELECT
[Property].Loanno,
[Property].[Balance amount],
COUNT(*) AS RowCount
FROM
[Property]
INNER JOIN
(
SELECT
Loanno,
MAX([Balance amount]) AS MaxBalance
FROM [Property]
GROUP BY Loanno
) AS Loanno_MaxBalance
ON Loanno_MaxBalance.Loanno=[Property].Loanno
AND Loanno_MaxBalance.MaxBalance=[Property].[Balance amount]
GROUP BY [Property].Loanno, [Property].[Balance amount]
So now this query stands on its own, and we don't need to keep [Loanno_MaxBalance] as a separate saved query in Access.
We'll save the above query as [Loanno_MaxBalance_Count].
Step 3a: Now to derive the [PropertyType] values using the [Property] table and the [Loanno_MaxBalance_Count] query:
SELECT DISTINCT
[Property].Loanno,
IIf(Loanno_MaxBalance_Count.RowCount>1, 8, [Property].PropertyType) AS PropertyType
FROM
[Property]
INNER JOIN
[Loanno_MaxBalance_Count]
ON [Property].Loanno=Loanno_MaxBalance_Count.Loanno
AND [Property].[Balance amount]=Loanno_MaxBalance_Count.[Balance amount]
Step 3b: Gee, that's not too bad at all. Let's "go for it" and replace the [Loanno_MaxBalance_Count] query reference with its SQL code (from Step 2b) as a subquery:
SELECT DISTINCT
[Property].Loanno,
IIf(Loanno_MaxBalance_Count.RowCount>1, 8, [Property].PropertyType) AS PropertyType
FROM
[Property]
INNER JOIN
(
SELECT
[Property].Loanno,
[Property].[Balance amount],
COUNT(*) AS RowCount
FROM
[Property]
INNER JOIN
(
SELECT
Loanno,
MAX([Balance amount]) AS MaxBalance
FROM [Property]
GROUP BY Loanno
) AS Loanno_MaxBalance
ON Loanno_MaxBalance.Loanno=[Property].Loanno
AND Loanno_MaxBalance.MaxBalance=[Property].[Balance amount]
GROUP BY [Property].Loanno, [Property].[Balance amount]
) AS Loanno_MaxBalance_Count
ON [Property].Loanno=Loanno_MaxBalance_Count.Loanno
AND [Property].[Balance amount]=Loanno_MaxBalance_Count.[Balance amount]
That's it! One self-contained query with no need for saved Access query dependencies.

MySQL, WHERE NOT IN very slow query?

SELECT product_id
FROM ebay_shop
LEFT JOIN amazon_shop ON ebay_shop.product_id = amazon_shop.product_id
WHERE ebay_shop.validated = 2007
AND amazon_shop.validated = 2007
Result(= 150 products): products are validated in ebay_shop && amazon_shop in year 2007
SELECT ebay_shop.product_id
FROM ebay_shop
WHERE ebay_shop.validated = 2010
Result(= 4000 products): products are validated in ebay_shop in year 2010
Question: i want to find products which aren't validated to 2010 of 2007 list, products have in 2007 but not in 2010.
My query:
SELECT product_id
FROM ebay_shop
LEFT JOIN amazon_shop ON ebay_shop.product_id = amazon_shop.product_id
WHERE ebay_shop.validated = 2007
AND amazon_shop.validated = 2007
AND NOT IN (SELECT ebay_shop.product_id
FROM ebay_shop
WHERE ebay_shop.validated = 2010)
My query is very slow(~60s) 150 find in 4000 records. I need help to improve this query.
NOT IN is terrible on performance. Probably the best way is to do a LEFT JOIN for your 2010 and then qualify it is NULL... Any record that IS found, throw it out, just keep the ones that return NULL via the join qualifier.
In addition, ensure you have an index on your ebay shop and amazon shops based on YEAR AND PRODUCT via index ( validated, product_id ). Reason to have the year first, to keep all the common year as small set, then cycle all products within that. Then, the index would also be optimized for the respective join too.
SELECT STRAIGHT_JOIN
es.product_id
FROM
ebay_shop eshop
LEFT JOIN amazon_shop ashop
ON eshop.validated = ashop.validated
AND eshop.product_id = ashop.product_id
LEFT JOIN ebay_shop ebay2
on ebay2.validated = 2010
and ebay2.product_id = eshop.product_id
WHERE
eshop.validated = 2007
and ebay2.validated is null
I realize this isn't really an answer but more of a different study case!
I also use "NOT IN" but in a different context. I get the result I what but the query takes more than 10 seconds for a table with about 300 records.
Here is the query:
(
SELECT (
'inscrit'
) AS
TYPE , membre_mandataire.id AS id, membres.prenom AS prenom, membres.nom_de_famille AS nom_de_famille, membre_mandataire.photo AS photo, membre_mandataire.niveau_qualification AS niveau_qualification, membre_mandataire.numero_mandataire AS numero_mandataire
FROM membres, membre_mandataire
WHERE membre_mandataire.id_membre = membres.id
AND membre_mandataire.confirme = 'Y'
AND membre_mandataire.visible_dans_liste = 'Y'
)
UNION (
SELECT (
'actif'
) AS
TYPE , mandataires_actifs.id AS id, mandataires_actifs.prenom AS prenom, mandataires_actifs.nom_de_famille AS nom_de_famille, mandataires_actifs.photo AS photo, mandataires_actifs.niveau_qualification AS niveau_qualification, mandataires_actifs.numero_mandataire AS numero_mandataire
FROM mandataires_actifs, membre_mandataire
WHERE (
mandataires_actifs.numero_mandataire
) NOT
IN (
SELECT membre_mandataire.numero_mandataire
FROM membre_mandataire
WHERE membre_mandataire.confirme = 'Y'
AND membre_mandataire.visible_dans_liste = 'Y'
)
)
ORDER BY nom_de_famille ASC
The field "numero_mandataire" is the constant in both tables. In the result, i Want all records from the table membre_mandataire and UNION or JOIN all records where "numero_mandataire" cannot be found in the table mandataires_actifs.
I guess the solution for me would be to go with "LEFT JOIN" instead of "UNION" but I am still stuck with "NOT IN". I've been trying for a while and I can't make it work.
I am learning MySQL but looks like i'm not quite there yet!
Thanks for any help!
Blaise
#DRapp wrote a fine workaround and I want to add an other idea...
SELECT group_concat(product_id)
FROM ebay_shop
LEFT JOIN amazon_shop ON ebay_shop.product_id = amazon_shop.product_id
WHERE ebay_shop.validated = 2007
AND amazon_shop.validated = 2007
and then:
SELECT product_id
FROM ebay_shop
LEFT JOIN amazon_shop ON ebay_shop.product_id = amazon_shop.product_id
WHERE ebay_shop.validated = 2007
AND amazon_shop.validated = 2007
AND NOT IN (1,2,3,4) /* results from first query */