Duplicate copies of SSRS report being delivered to subscriber - reporting-services

Several SSRS subscribers are receiving duplicate copies of reports
Confirmed there are not multiple subscriptions
NA
I expect delivery of a single copy of the SSRS report to the subscriber

You could query the execution log to ascertain if there are any hidden executions of the report. Here is a script I use to achieve a similar thing.
Connect to your report server database, or your sharepoint integrated version whichever applies.
WITH subscriptionXmL
AS
(SELECT
SubscriptionID, OwnerID, Report_OID, Locale, InactiveFlags,
ExtensionSettings, CONVERT(XML, ExtensionSettings) AS ExtensionSettingsXML,
ModifiedByID, ModifiedDate, Description, LastStatus, EventType, MatchData,
LastRunTime, Parameters, DeliveryExtension, Version
FROM dbo.Subscriptions
),
-- Get the settings as pairs
SettingsCTE
AS
(SELECT
SubscriptionID ,
ExtensionSettings ,
-- include other fields if you need them.
ISNULL(Settings.value('(./*:Name/text())[1]', 'nvarchar(1024)'), 'Value') AS SettingName,
Settings.value('(./*:Value/text())[1]', 'nvarchar(max)') AS SettingValue,
Report_OID, LastRunTime, LastStatus, InactiveFlags
FROM
subscriptionXmL
CROSS APPLY subscriptionXmL.ExtensionSettingsXML.nodes('//*:ParameterValue') Queries ( Settings ))
select distinct
ssrs.name,
ssrs.Path,
--convert(varchar(max),ReportQry) ReportQry,
[LastExec] [SQL Last Executed],
subs.SettingName,
subs.SettingValue [Recipients],
InactiveFlags
from
(SELECT top (100) percent Name,
CAST(CAST(Content AS VARBINARY(MAX)) AS XML) AS ReportQry,
ItemID [Report_ID],
Path
FROM dbo.Catalog c
) ssrs
-- grab the last executed date
left join (select max(TimeStart) [LastExec], ReportID from ExecutionLog ex group by ReportID)ex
on ssrs.[Report_ID] = ex.ReportID
left join (
SELECT *
FROM
(select dbo.Catalog.Name, SettingsCTE.SubscriptionID, SettingsCTE.SettingName, SettingValue, LastRunTime, LastStatus, Report_OID, InactiveFlags
FROM
SettingsCTE JOIN dbo.Catalog on SettingsCTE.Report_OID = dbo.Catalog.ItemID
WHERE
settingName IN ( 'TO', 'CC', 'BCC' )
AND SettingValue IS NOT NULL
) subs
)subs
on ssrs.Report_ID = subs.Report_OID
where
-- filter down to a report that references a particular object name
(convert(varchar(max),ReportQry) LIKE '%you object / table / view%')
-- or filter down to an individual report
OR ssrs.name like '%your repot name%'

Related

Table is specified twice, both as a target for 'UPDATE' and as a separate source for data in mysql

I have below query in mysql where I want to check if branch id and year of finance type from branch_master are equal with branch id and year of manager then update status in manager table against branch id in manager
UPDATE manager as m1
SET m1.status = 'Y'
WHERE m1.branch_id IN (
SELECT m2.branch_id FROM manager as m2
WHERE (m2.branch_id,m2.year) IN (
(
SELECT DISTINCT branch_id,year
FROM `branch_master`
WHERE type = 'finance'
)
)
)
but getting error
Table 'm1' is specified twice, both as a target for 'UPDATE' and as a
separate source for data
This is a typical MySQL thing and can usually be circumvented by selecting from the table derived, i.e. instead of
FROM manager AS m2
use
FROM (select * from manager) AS m2
The complete statement:
UPDATE manager
SET status = 'Y'
WHERE branch_id IN
(
select branch_id
FROM (select * from manager) AS m2
WHERE (branch_id, year) IN
(
SELECT branch_id, year
FROM branch_master
WHERE type = 'finance'
)
);
The correct answer is in this SO post.
The problem with here accepted answer is - as was already mentioned multiple times - creating a full copy of the whole table. This is way far from optimal and the most space complex one. The idea is to materialize the subset of data used for update only, so in your case it would be like this:
UPDATE manager as m1
SET m1.status = 'Y'
WHERE m1.branch_id IN (
SELECT * FROM(
SELECT m2.branch_id FROM manager as m2
WHERE (m2.branch_id,m2.year) IN (
SELECT DISTINCT branch_id,year
FROM `branch_master`
WHERE type = 'finance')
) t
)
Basically you just encapsulate your previous source for data query inside of
SELECT * FROM (...) t
Try to use the EXISTS operator:
UPDATE manager as m1
SET m1.status = 'Y'
WHERE EXISTS (SELECT 1
FROM (SELECT m2.branch_id
FROM branch_master AS bm
JOIN manager AS m2
WHERE bm.type = 'finance' AND
bm.branch_id = m2.branch_id AND
bm.year = m2.year) AS t
WHERE t.branch_id = m1.branch_id);
Note: The query uses an additional nesting level, as proposed by #Thorsten, as a means to circumvent the Table is specified twice error.
Demo here
Try :::
UPDATE manager as m1
SET m1.status = 'Y'
WHERE m1.branch_id IN (
(SELECT DISTINCT branch_id
FROM branch_master
WHERE type = 'finance'))
AND m1.year IN ((SELECT DISTINCT year
FROM branch_master
WHERE type = 'finance'))
The problem I had with the accepted answer is that create a copy of the whole table, and for me wasn't an option, I tried to execute it but after several hours I had to cancel it.
A very fast way if you have a huge amount of data is create a temporary table:
Create TMP table
CREATE TEMPORARY TABLE tmp_manager
(branch_id bigint auto_increment primary key,
year datetime null);
Populate TMP table
insert into tmp_manager (branch_id, year)
select branch_id, year
from manager;
Update with join
UPDATE manager as m, tmp_manager as tmp_m
inner JOIN manager as man on tmp_m.branch_id = man.branch_id
SET status = 'Y'
WHERE m.branch_id = tmp_m.branch_id and m.year = tmp_m.year and m.type = 'finance';
This is by far the fastest way:
UPDATE manager m
INNER JOIN branch_master b on m.branch_id=b.branch_id AND m.year=b.year
SET m.status='Y'
WHERE b.type='finance'
Note that if it is a 1:n relationship the SET command will be run more than once. In this case that is no problem. But if you have something like "SET price=price+5" you cannot use this construction.
Maybe not a solution, but some thoughts about why it doesn't work in the first place:
Reading data from a table and also writing data into that same table is somewhat an ill-defined task. In what order should the data be read and written? Should newly written data be considered when reading it back from the same table? MySQL refusing to execute this isn't just because of a limitation, it's because it's not a well-defined task.
The solutions involving SELECT ... FROM (SELECT * FROM table) AS tmp just dump the entire content of a table into a temporary table, which can then be used in any further outer queries, like for example an update query. This forces the order of operations to be: Select everything first into a temporary table and then use that data (instead of the data from the original table) to do the updates.
However if the table involved is large, then this temporary copying is going to be incredibly slow. No indexes will ever speed up SELECT * FROM table.
I might have a slow day today... but isn't the original query identical to this one, which souldn't have any problems?
UPDATE manager as m1
SET m1.status = 'Y'
WHERE (m1.branch_id, m1.year) IN (
SELECT DISTINCT branch_id,year
FROM `branch_master`
WHERE type = 'finance'
)

Unique values concatenate with a comma separated value in SQL Server 2014 per ID

I have two tables in SQL Server 2014:
current_entry
data_by_client
I have two columns User_ and Skill. I need unique Skill values to concatenate from both the tables in the select statement as per the User Ids. String_Agg doesn't work in SQL Server 2014, so I tried using XML PATH.
Please find the attached screenshot for the expected output :
Here is the SQL Fiddle for it : SQL Fiddle
I have used the below XML PATH query :
SELECT
User_, Products + Skill
FROM
(SELECT DISTINCT
User_, Skill,
(SELECT DISTINCT rs.Skill + ','
FROM dbo.current_entry u
INNER JOIN dbo.data_by_client rs ON u.User_ = rs.User_
WHERE rs.User_ = r.User_
FOR XML PATH('')) AS Products
FROM
dbo.current_entry r) l
I am facing issues as I am not getting the expected output.
Can you try to workout your solution using the foll. Approach:
SELECT
t1.user,
Item = stuff((SELECT ( ', ' + skill )
FROM #string_test t2
WHERE t1.user = t2.user
ORDER BY 1
FOR XML PATH( '' )
), 1, 1, '' )FROM #string_test t1
GROUP BY user
Reference: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/4ac19fe5-be05-479f-bcc2-8fdfa6c1ae1f/pivot-string-concatenation?forum=transactsql
You can then use custom sql function to remove duplicates. Refer to http://blog.sqlauthority.com/2009/01/15/sql-server-remove-duplicate-entry-from-comma-delimited-string-udf/amp/

Passing a list into MySQL stored procedure and check ALL values are present

I have a query, where I check the user has ALL the permissions present in a list of perms.
So, it's something like this...
SELECT DISTINCT account_id
FROM pp_acl_user_roles ur, pp_acl_role_permissions rp
JOIN pp_acl_permissions p ON rp.permission_id=p.id
WHERE (
ur.account_id = '1'
#check for permission ids OR keys depending on what has been passed in.
AND ( p.id IN ('read_accounts', 'write_accounts') OR p.key IN ('read_accounts', 'write_accounts') )
AND ur.role_id = rp.role_id
)
#ensure we have ALL the permissions we asked for, not just some -makes IN() an AND not an OR.
GROUP BY ur.account_id
HAVING COUNT(DISTINCT rp.permission_id) = 2
It checks for either a list of ids or a list of keys for the permissions, so it could be called with either, so this line.
p.id IN ('read_accounts', 'write_accounts') OR p.key IN ('read_accounts', 'write_accounts')
could be
p.id IN (1, 2) OR p.key IN (1, 2)
depending on how it's called.
The HAVING at the end ensures we matched all the items we asked for.
This is fine, but I want to move it to a stored procedure, and I'm hitting a issue.
I had to change IN for FIND_IN_SET so I could pass a string list into the procedure, but the problem is, I have no way to dynamically calculate how many items are in the list, so I can't check they are all present.
Iv'e got this so far...
CREATE PROCEDURE has_permission( IN account_id BIGINT, IN permissions TEXT )
BEGIN
SELECT DISTINCT account_id
FROM pp_acl_user_roles ur, pp_acl_role_permissions rp
JOIN pp_acl_permissions p ON rp.permission_id=p.id
WHERE (
ur.account_id = account_id
#check for permission ids OR keys depending on what has been passed in.
AND ( FIND_IN_SET(p.id, permissions) OR FIND_IN_SET(p.key, permissions) )
AND ur.role_id = rp.role_id
)
#ensure we have ALL the permissions we asked for, not just some -makes IN() an AND not an OR.
GROUP BY ur.account_id;
HAVING COUNT(DISTINCT rp.permission_id) = ????????????
END //
DELIMITER ;
But there is no way to calculate the length of the permissions passed in.
I'm sure there is a way to maybe JOIN to the perms table based on the items in the string and ensure we have matches in both tables, but I cant work it out.
Any pointers much appreciated...
This is what I've got so far...
Not ideal, but you can work out the length of the items in the string with this..
(SELECT LENGTH(permissions) - LENGTH( REPLACE(permissions, ',', '') ) + 1)
It basically counts all the commas in the string and uses that as the total number of perms passed in.
CREATE PROCEDURE has_permission( IN account_id BIGINT, IN permissions TEXT)
BEGIN
SELECT DISTINCT account_id
FROM pp_acl_user_roles ur, pp_acl_role_permissions rp
JOIN pp_acl_permissions p ON rp.permission_id=p.id
WHERE (
ur.account_id = account_id
#check for permission ids OR keys depending on what has been passed in.
AND ( FIND_IN_SET(p.id, permissions) OR FIND_IN_SET(p.key, permissions) )
AND ur.role_id = rp.role_id
)
#ensure we have ALL the permissions we asked for, not just some -makes IN() an AND not an OR.
GROUP BY ur.account_id
HAVING COUNT(DISTINCT rp.permission_id) = (SELECT LENGTH(permissions) - LENGTH( REPLACE(permissions, ',', '') ) + 1);

SQL Server 2008 - Avoiding a foreach loop

I am trying desperately to avoid a foreach situation in SQL Server 2008 (my background is in c#).
Basically, I have a list of SKUs. For each SKU in the list, I need to perform some calculations that determine if that particular SKU will be displayed on the web.
To get my list of SKUs, I use this:
SELECT Feed.StyleCode as SKU
FROM [eCommerce].[dbo].[BABW_ItemFeed] as Feed
WHERE Feed.ProductDefinition = 'Kit'
Returning this:
And to calculate each SKUs fields, I've been using this:
DECLARE #SKU AS varchar(50)
SET #SKU= '11993_16559_16227'
SELECT
#SKU as SKU,
0 AS Quantity,
MIN(ISNULL(Sending.IsActive, 'WEBNO')) AS IsActive,
MAX(ISNULL(Sending.IsDiscontinued, 1)) AS IsDiscontinued
FROM
(
SELECT * FROM [eCommerce].[dbo].[Split] (
#SKU
,'_')
) AS SplitSkus
LEFT JOIN #SkusToSend AS Sending
ON Sending.SKU = SplitSkus.items
Returning this:
Now I need to synch the two tables together, removing the #SKU declaration. I don't think I'm able to use a UNION to do this, because the second function requires fore-knowledge of the SKU it will be processing... and a JOIN would require something to join on, which I don't really have. Is there some function I'm not familiar with that I can use to create a complete table of SKUs in one go without a looping mechanism?
Try a CROSS APPLY... which will execute your UDF for each row in BABW_ItemFeed:
SELECT
Feed.StyleCode as SKU,
COUNT(*) AS Quantity,
MIN(ISNULL(Sending.IsActive, 'WEBNO')) AS IsActive,
MAX(ISNULL(Sending.IsDiscontinued, 1)) AS IsDiscontinued
FROM
[eCommerce].[dbo].[BABW_ItemFeed] as Feed
CROSS APPLY [eCommerce].[dbo].[Split] (Feed.StyleCode, '_') AS SplitSkus
LEFT JOIN #SkusToSend AS Sending
ON Sending.SKU = SplitSkus.items
WHERE
Feed.ProductDefinition = 'Kit'
GROUP BY
Feed.StyleCode
Stop using Min() and Max()...or else, pull SKU (don't use the parameter in the SELECT).
Try This:
SELECT
SKU,
0 AS Quantity,
MIN(ISNULL(Sending.IsActive, 'WEBNO')) AS IsActive,
MAX(ISNULL(Sending.IsDiscontinued, 1)) AS IsDiscontinued
FROM
(
SELECT [eCommerce].[dbo].[Split] (Feed.StyleCode,'_') as SKU
FROM [eCommerce].[dbo].[BABW_ItemFeed] as Feed
WHERE Feed.ProductDefinition = 'Kit'
) AS SplitSkus
LEFT JOIN #SkusToSend AS Sending
ON Sending.SKU = SplitSkus.items

Why does my indexed column appear not to have statistics?

I'm using SQL Server and I'm currently trying to debug some queries where the optimizer has chosen a poor execution plan and I noticed for one of my indexed columns that when I run the command:
DBCC SHOW_STATISTICS ("tablename", columnname);
for this indexed column, the database returns:
Could not locate statistics 'columnname' in the system catalogs.
According to this page:
http://msdn.microsoft.com/en-us/library/ms190397.aspx
"The query optimizer creates statistics for indexes on tables or views when the index is created."
I also have AUTO_CREATE_STATISTICS on.
Should I have to manually have to run a CREATE STATISTICS for this column? If so, since it's an index shouldn't it already have statistics for the column?
From https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-show-statistics-transact-sql:
If target is the name of an existing column, and an automatically
created statistics on this column exists, information about that
auto-created statistic is returned. If an automatically created
statistic does not exist for a column target, error message 2767 is
returned.
So specifying the name of the index for target (the second parameter) will work, but it won't work if you use the column name.
If you run this (credit to Erland Sommarskog, http://www.sommarskog.se/query-plan-mysteries.html), you can see if stats were auto-created or not:
DECLARE #tbl NVARCHAR(256)
SELECT #tbl = 'tableName'
SELECT
o.name, s.stats_id, s.name, s.auto_created, s.user_created,
SUBSTRING(scols.cols, 3, LEN(scols.cols)) AS stat_cols,
STATS_DATE(o.object_id, s.stats_id) AS stats_date,
s.filter_definition
FROM
sys.objects o JOIN
sys.stats s ON s.object_id = o.object_id
CROSS APPLY
(
SELECT ', ' + c.name
FROM
sys.stats_columns sc JOIN
sys.columns c ON sc.object_id = c.object_id AND sc.column_id = c.column_id
WHERE sc.object_id = s.object_id AND sc.stats_id = s.stats_id
ORDER BY sc.stats_column_id
FOR XML PATH('')
) AS scols(cols)
WHERE o.name = #tbl
ORDER BY o.name, s.stats_id