<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>SQL Server in Enterprise</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/shiyangqiu/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/shiyangqiu/feed.xml</link>
    <description>↑↑↓↓←→←→BA</description>
    <lastBuildDate>Tue, 16 Apr 2019 05:28:52 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Check the Available Memory of Default Pool</title>
      <link>https://docs.microsoft.com/archive/blogs/shiyangqiu/check-the-available-memory-of-default-pool</link>
      <pubDate>Thu, 30 Mar 2017 18:03:24 GMT</pubDate>
      <dc:creator><![CDATA[Shiyang Qiu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/shiyangqiu/?p=475</guid>
      <description><![CDATA[One capacity planning best practice of using SQL In-Memory OLTP engine is to bind the database with...]]></description>
      <content:encoded><![CDATA[One capacity planning best practice of using SQL In-Memory OLTP engine is to <a href="https://docs.microsoft.com/en-us/sql/relational-databases/in-memory-oltp/bind-a-database-with-memory-optimized-tables-to-a-resource-pool">bind the database with a resource governance pool.</a> The calculation of memory resource of pools is from <a href="https://docs.microsoft.com/en-us/sql/relational-databases/resource-governor/resource-governor-resource-pool">here</a>, and I translate it into below TSQL code  to check both configured value and available value in run time.
<!--more-->

[sql]
declare @MinMemoryPercent  decimal(4,3)
declare @MaxAvailableMemoryKB bigint
declare @InusedMemoryKB bigint
declare @ResPoolMinMemoryCapKB bigint
declare @AvailableSharedMemoryKB bigint

select @MinMemoryPercent  = sum(min_memory_percent)/100, @InusedMemoryKB = sum(used_memory_kb)  from sys.dm_resource_governor_resource_pools

select @MaxAvailableMemoryKB = available_physical_memory_kb  from sys.dm_os_sys_memory 

set @ResPoolMinMemoryCapKB = @MinMemoryPercent * @MaxAvailableMemoryKB


if(@InusedMemoryKB&gt;@ResPoolMinMemoryCapKB) set @AvailableSharedMemoryKB = @MaxAvailableMemoryKB - @InusedMemoryKB
else set @AvailableSharedMemoryKB = @MaxAvailableMemoryKB - @ResPoolMinMemoryCapKB

select 
@MaxAvailableMemoryKB/1024.0 as [Max_Available_Memory_MB]
,@InusedMemoryKB/1024.0 as [Inused_Memory_MB]
,@ResPoolMinMemoryCapKB/1024.0 as [Resource_Pool_Min_Memory_Cap_MB]
,@AvailableSharedMemoryKB /1024.0 as [Available_Shared_Memory_MB]

[/sql]

-------------------------------------------
Posted by Shiyang Qiu, 2017 Mar 30
Thanks to Frankie Lai for peer review.]]></content:encoded>
    </item>
    <item>
      <title>SQL16: Database in Delete-Only Mode</title>
      <link>https://docs.microsoft.com/archive/blogs/shiyangqiu/sql16-database-in-delete-only-mode</link>
      <pubDate>Thu, 23 Mar 2017 16:19:28 GMT</pubDate>
      <dc:creator><![CDATA[Shiyang Qiu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/shiyangqiu/?p=465</guid>
      <description><![CDATA[What will happen when the disk of In-memory OLTP checkpoint file pair is full in SQL Server 2016?...]]></description>
      <content:encoded><![CDATA[What will happen when the disk of In-memory OLTP checkpoint file pair is full in SQL Server 2016? Here is the learning from a recent test:

<strong>#1 Change in SQL 2016</strong>
This is a change in SQL Server 2016. We will mark the database in delete-only mode (DOM) for memory-optimized tables when we are out of storage space. In SQL Server 2014, the user database will be suspended, and now it could still be online. SQL Error 41822 will be raised when new insert is triggered.

Msg 41822, Level 17, State 1, line 5
There is insufficient disk space to generate checkpoint files and as a result the database is in delete-only mode. In this mode, only deletes and drops are allowed.
<strong>#2 How to tell database is in a delete-only mode</strong>
In current version of SQL Server, you can check by sending a non delete statement to user database.
<strong>#3 How to resume the insert</strong>
After you deleting records or freeing more disk space, you can run a manual CHECKPOINT command to bring database back to normal from Delete-Only Mode. Or it will be back to normal by next auto CHECKPOINT.

&nbsp;

Posted by Shiyang Qiu, Mar 23, 2017]]></content:encoded>
    </item>
    <item>
      <title>DBCC CLONEDATABASE can not Copy Encrypted Objects</title>
      <link>https://docs.microsoft.com/archive/blogs/shiyangqiu/dbcc-clonedatabase-can-not-copy-encrypted-objects</link>
      <pubDate>Fri, 18 Nov 2016 14:31:28 GMT</pubDate>
      <dc:creator><![CDATA[Shiyang Qiu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/shiyangqiu/?p=425</guid>
      <description><![CDATA[There is a new command DBCC CLONEDATABASE from latest SQL Server 2014 SP2, SQL Server 2016 SP1...]]></description>
      <content:encoded><![CDATA[There is a new command <strong>DBCC CLONEDATABASE</strong> from latest SQL Server 2014 SP2, SQL Server 2016 SP1 release. It's designed to collect the database objects definition, index statistic and query store info without table data. It's useful when support team need to investigate the performance issue like suboptimal execution plan remotely. Using <strong>DBCC CLONEDATABASE</strong> could keep all index stats and query store beside the real production data which may be sensitive or with huge size. It may be used to keep a version of database without index stats or query store info. For more detail you could check <a href="https://support.microsoft.com/en-us/kb/3177838">KB 3177838</a>.

&nbsp;

Beside what has been documented in KB 3177838, there is one known non supported object, the encrypted objects. There will be no warning when you run the command, the encrypted objects will be copied but there will be error when calling the encrypted objects in cloned database. Make sure you provide the decrypted plain text if that object what you need to investigate or keep the source in other way.

&nbsp;

Posted by Shiyang Qiu, Nov 18, 2016
Special thanks to Parikshit Savjani for KB 3177838 revision]]></content:encoded>
    </item>
    <item>
      <title>SQL16 Full Backup Size Difference on Primary Replica and Secondary Replica</title>
      <link>https://docs.microsoft.com/archive/blogs/shiyangqiu/sql16-full-backup-size-difference-on-primary-replica-and-secondary-replica</link>
      <pubDate>Sun, 16 Oct 2016 15:56:45 GMT</pubDate>
      <dc:creator><![CDATA[Shiyang Qiu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/shiyangqiu/?p=415</guid>
      <description><![CDATA[Database full backup could be taken from Always On Availability Groups Active Secondary with...]]></description>
      <content:encoded><![CDATA[Database full backup could be taken from Always On Availability Groups Active Secondary with COPY_ONLY option. In a recent SQL2016 project, we observe that the copy only full backup size are different when taking backup from Primary Replica and Secondary Replica.
<!--more-->
<h1>Symptom</h1>
We configure 3-nodes AlwaysOn AG with SQL Server 2016 CU1 64bit. Synced mode is used for every replica. The status of each replica is synchronized. Before full backup task, there are many data modification. There is no other workload during the whole test. The backup file size and backup duration are different. When we analyze the backup statement output, we find out the number of processed page for log on Primary is much larger than on Secondary.

<strong>Copy_only Full Backup Statement Output on Primary Replica</strong>
Processed 6216 pages for database 'testing_db', file 'testing_db_data' on file 1.
Processed 3979919 pages for database 'testing_db', file 'testing_db_dir' on file 1.
Processed 16333281 pages for database 'testing_db', file 'testing_db_log' on file 1.
BACKUP DATABASE successfully processed 20319416 pages in 170.540 seconds (930.839 MB/sec).

<strong>Copy_only Full Backup <b>Statement Output</b> on Secondary Replica</strong>
Processed 6216 pages for database 'testing_db', file 'testing_db_data' on file 1.
Processed 3973037 pages for database 'testing_db', file 'testing_db_dir' on file 1.
Processed 1334436 pages for database 'testing_db', file 'testing_db_log' on file 1.
BACKUP DATABASE successfully processed 5313689 pages in 40.881 seconds (1015.464 MB/sec).

&nbsp;
<h1>Why the Processed Pages of Log are Different</h1>
Data pages and log pages are copied into the backup set when full backup are being executed. The main reason of copying log is to make sure we could restore a backup file to get a transactionally consistent database. And <a href="https://www.sqlskills.com/blogs/paul/debunking-a-couple-of-myths-around-full-database-backups/">a full database backup contains more than the transaction log from the start of the backup to the end of the backup.</a> After reviewing the code of current SQL Server, here is how SQL Server 2016 decides which range of log a full backup will contain:
<table style="border: solid black 0.75pt">
<tbody>
<tr>
<td width="288" style="border: solid black 0.75pt"><strong>Full Backup Type:</strong></td>
<td width="288" style="border: solid black 0.75pt"><strong>The start LSN of the log included in a database backup is the minimum of:</strong></td>
</tr>
<tr>
<td width="288" style="border: solid black 0.75pt">Normal Full Backup on Primary Replica:</td>
<td width="288" style="border: solid black 0.75pt">m_ckptLSNm_OldestPageLSN

m_oldActXact

m_replLSN

m_dbMirroringLSN

m_HostLogLSN

m_HkRecoveryLSN

<strong>m_HADRonLSN</strong></td>
</tr>
<tr>
<td width="288" style="border: solid black 0.75pt">COPY_ONLY Full Backup on Primary Replica:</td>
<td width="288" style="border: solid black 0.75pt">m_ckptLSNm_OldestPageLSN

m_oldActXact

m_replLSN

m_dbMirroringLSN

m_HostLogLSN

m_HkRecoveryLSN

<strong>m_HADRonLSN</strong></td>
</tr>
<tr>
<td width="288" style="border: solid black 0.75pt">COPY_ONLY Full Backup on Secondary Replica:</td>
<td width="288" style="border: solid black 0.75pt">m_ckptLSNm_OldestPageLSN

m_oldActXact

m_replLSN

m_dbMirroringLSN

m_HostLogLSN

m_HkRecoveryLSN</td>
</tr>
</tbody>
</table>
&nbsp;

SQL Server will calculate the start LSN of the log included in a database backup by above table. Normal full backup and copy_only full backup count same set of internal LSN. Comparing to copy_only full backup on Secondary, <strong>m_HADRonLSN</strong> is included on Primary. This is the reason why backup size on Primary is larger than Secondary.

&nbsp;

<strong>m_HADRonLSN</strong> is mainly to record the min LSN that has not been shipped to replicas. <strong>m_HADRonLSN</strong> is the min of all the secondary truncation points which include all the truncation points. From SQL 2016, <strong>m_HADRonLSN</strong> is also used in the Secondary fast seeding. Taking log backup will also move <strong>m_HADRonLSN</strong> forward when all the secondary replicas are in synced status.
<h1>Suggestion</h1>
Basing on the finding, if there is no differential backup in your backup plan, you can take COPY_ONLY full backup on Secondary to get better backup size. If the full backup is scheduled on Primary (when you need differential backup or have no license for readable secondary feature), you could to also schedule the log backup first to move <strong>m_HADRonLSN</strong> forward.

&nbsp;

&nbsp;

Posted by Shiyang Qiu, Oct 16, 2016
Special thanks to Simon Su for the SQL source code review.]]></content:encoded>
    </item>
    <item>
      <title>Reporting Service Execution Log Retention</title>
      <link>https://docs.microsoft.com/archive/blogs/shiyangqiu/reporting-service-execution-log-retention</link>
      <pubDate>Mon, 12 Sep 2016 16:25:54 GMT</pubDate>
      <dc:creator><![CDATA[Shiyang Qiu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/shiyangqiu/?p=386</guid>
      <description><![CDATA[The default logging retention value is 60 days in SSRS. If you want to modify the retention period,...]]></description>
      <content:encoded><![CDATA[The default logging retention value is 60 days in SSRS. If you want to modify the retention period, you could use SQL Server Management Studio to connect to SSRS instance, check the <strong>Advanced</strong> page of <strong>Server Properties</strong>, and change the value of <strong>ExecutionLogDaysKept</strong>.

From the GUI it reads “<em>Valid values for this property include -1 and 0 through 2,147,483,647. If the value is 0 entries are not deleted from the Execution Log Table.</em>” SSRS Product Group confirm there is a known issue that only -1 will keep the log indefinitely. Value 0 means keep 0 day of log; the log will be deleted when the retention job is trigger.

<a href="https://msdnshared.blob.core.windows.net/media/2016/09/retetion1.png"><img width="1024" height="884" class="alignnone size-large wp-image-395" alt="retetion1" src="https://msdnshared.blob.core.windows.net/media/2016/09/retetion1-1024x884.png" /></a>
&nbsp;

Every day at 2AM local time, the retention job will be triggered from each SSRS server to archive the log. Stored Proc <strong>ExpireExecutionLogEntries</strong> in SSRS catalog database is called. If you change the SSRS web/app server time zone setting, you should restart SSRS web/app server to make the retention job running in the right time. 

<a href="https://msdnshared.blob.core.windows.net/media/2016/09/retetion2.png"><img width="1024" height="293" class="alignnone size-large wp-image-405" alt="retetion2" src="https://msdnshared.blob.core.windows.net/media/2016/09/retetion2-1024x293.png" /></a>
&nbsp;
Let’s check the coding of this stored proc:

[sql]
if exists (select * from ConfigurationInfo where [Name] = 'ExecutionLogDaysKept' and CAST(CAST(Value as nvarchar) as integer) = -1)
begin
return
end

delete from ExecutionLogStorage
where DateDiff(day, TimeStart, getdate()) &gt;= (select CAST(CAST(Value as nvarchar) as integer) from ConfigurationInfo where [Name] = 'ExecutionLogDaysKept')
[/sql]

You will see only -1 could keep the log indefinitely.
&nbsp;

For a heavy production environment, we don’t recommend you keeping the log forever. Because it will increase the size of SSRS’s catalog database. In my testing environment, size of log table with 1,572,866 log entries is 900MB. When the retention job is running to delete huge number of log, it takes time and there will be exclusive table lock on table <strong>ExecutionLogStorage</strong>. No report generation will be done before the lock is released. In my testing environment (Azure Standard D2 v2 VM), it takes 112 seconds to delete 1,572,866 log entries. And it causes report execution error. So you should avoid schedule the regular report subscription at 2AM.

&nbsp;

Posted by Shiyang Qiu, Sep 12, 2016
Special thanks to Chris Finlan]]></content:encoded>
    </item>
    <item>
      <title>SQL16 Reporting Service Execution Log for Mobile Report</title>
      <link>https://docs.microsoft.com/archive/blogs/shiyangqiu/sql16-reporting-service-execution-log-for-mobile-report</link>
      <pubDate>Wed, 31 Aug 2016 17:45:47 GMT</pubDate>
      <dc:creator><![CDATA[Shiyang Qiu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/shiyangqiu/?p=345</guid>
      <description><![CDATA[Update: 

SQL 2016 SP1 makes the change!

One area of feedback we rec’d was around the inability...]]></description>
      <content:encoded><![CDATA[Update: 

SQL 2016 SP1 makes the change!
<em>
One area of feedback we rec’d was around the inability to see any logs in the report catalog related to specific mobile reports.  With this release, you now can see basic information in the execution logs for mobile reports, including the folder path, report name, user who ran the report and when they ran it.</em>

<a href="https://blogs.msdn.microsoft.com/sqlrsteamblog/2016/11/16/whats-new-in-sql-server-2016-sp1-for-reporting-services/">https://blogs.msdn.microsoft.com/sqlrsteamblog/2016/11/16/whats-new-in-sql-server-2016-sp1-for-reporting-services/</a>



I feel proud that my feedback makes SQL better.
Shiyang 
Nov 17, 2016
********************************************************************************************


SQL Server Reporting Services provide a built-in log function to store rich information of execution history of the report. That information could be used to create performance baseline; trouble shoot performance etc. You could check more information from: <a href="https://msdn.microsoft.com/en-us/library/ms159110.aspx">Report Server ExecutionLog and the ExecutionLog3 View.</a>
<h1>What's new for SQL 2016 Mobile Report Execution Logging</h1>
From SQL 2016, Microsoft introduce <a href="https://blogs.technet.microsoft.com/dataplatforminsider/2015/10/29/microsoft-business-intelligence-our-reporting-roadmap/">a new type of mobile report which bases on Datazen.</a> In SQL 2016 RTM, the execution logging granularity of mobile report is embedded server side dataset. It's different from paginated report execution logging, which logging granularity is report.

Every time we generate a mobile report with data refresh, for every dataset inside the mobile report, SSRS will process the dataset from data source, then render it as JSON to client app. There will be 2 log records, the 1st record has no value of <strong>ItemPath</strong>, which Format is <strong>SHAREDDATASETJSON</strong>. The 2nd record has no value of Format, which <strong>ItemPath</strong> is the path of dataset, <strong>TimeRendering</strong> is always 0. They share the same <strong>ExecutionId</strong>.

<a href="https://msdnshared.blob.core.windows.net/media/2016/08/MobileReportExecutionLog1.png"><img width="1024" height="225" class="alignnone size-large wp-image-355" alt="MobileReportExecutionLog1" src="https://msdnshared.blob.core.windows.net/media/2016/08/MobileReportExecutionLog1-1024x225.png" /></a>
<h1>Current recommendation</h1>
My recommendation is to apply the naming rule of dataset: put mobile report name as prefix. Then you could find out the execution basing on the name of dataset and <strong>ExecutionId</strong>.

Below is one example:

[sql]
use ReportServer
go

select [InstanceName]
,max([ItemPath]) as [ItemPath]
,[UserName]
,[ExecutionId]
,min([TimeStart]) as [FirstTimeStart]
,max([TimeEnd]) as [LastTimeEnd]
,max([TimeDataRetrieval]) as [TotalTimeDataRetrieval]
,sum([TimeProcessing]) as [TotalTimeProcessing]
,sum([TimeRendering]) as [TotalTimeRendering]
,sum([ByteCount]) as [TotalByteCount]
,max([RowCount])
from dbo.ExecutionLog3
group by [InstanceName],[ExecutionId],[UserName]
having max([Format]) = 'SHAREDDATASETJSON'
order by [FirstTimeStart] asc
go

[/sql]



Posted by Shiyang Qiu, Aug 31, 2016
Special thanks to Chris Finlan]]></content:encoded>
    </item>
    <item>
      <title>Data Warehouse, Clustered Columnstore Indexes and SAS</title>
      <link>https://docs.microsoft.com/archive/blogs/shiyangqiu/data-warehouse-clustered-columnstore-indexes-and-sas</link>
      <pubDate>Fri, 12 Aug 2016 14:50:50 GMT</pubDate>
      <dc:creator><![CDATA[Shiyang Qiu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/shiyangqiu/?p=285</guid>
      <description><![CDATA[Clustered Columnstore Indexes (CCI) is a game changer for data warehouse workload, which is...]]></description>
      <content:encoded><![CDATA[<a href="https://msdn.microsoft.com/library/dn934994.aspx">Clustered Columnstore Indexes (CCI)</a> is a game changer for data warehouse workload, which is available from SQL Server 2014 Enterprise Edition. I worked for a PoC project last year to adopt Clustered Columnstore Indexes for data warehouse. The PoC method is simple but the improvement is very significant. Below is some detailed.

<h1>The Pain and the Result</h1>
Customer run a 353GB data warehouse on SQL 2008 R2, max memory is 20GB. The ELT tool is SAS, SSAS cube is built on top of data warehouse. The SSAS model is changed frequently and full process is ran every day due to their dynamical business nature. As the physical memory size is small, there is heavy IO loading to storage during data process and customer plan to increase physical memory to 200GB. 

By changing the top 30 tables to CCI tables, the database size reduces from 353GB to 75GB, the largest table size reduces from 46GB to 4.5GB! The duration of daily ELT reduces from 35 seconds to 121 seconds when the SQL max memory is 20GB, reduces from 292 seconds to 49 seconds when the SQL max memory is 200GB. The duration of cube process reduces from 35 min to 15 min when the SQL max memory is 20GB, reduces from 21 min to 14 min when the SQL max memory is 200GB. So we are happy to see the huge improvement after adopting CCI without new hardware expense.

<h1>Migration Method</h1>
<h2>Step 1: Find out top tables</h2>
In this PoC there is no unsupported data type, no unique key, primary key or foreign key in every fact table as it's a well design data warehouse, I use below SQL find out top 30 tables in terms of size:
[sql]
use [db_name]
go

select top 30
	i.object_id,
    t.name as table_name,
    s.name as [schema_name],
    p.rows as rowc_ounts,
    sum(a.total_pages) / 128.0 as total_space_mb, 
    sum(a.used_pages) / 128.0 as used_space_mb, 
    (sum(a.total_pages) - sum(a.used_pages)) / 128.0 as unused_space_mb
into #t_top_tables
from 
    sys.tables t
inner join      
    sys.indexes i on t.object_id = i.object_id
inner join 
    sys.partitions p on i.object_id = p.object_id and i.index_id = p.index_id
inner join 
    sys.allocation_units a on p.partition_id = a.container_id
left outer join 
    sys.schemas s on t.schema_id = s.schema_id
where 
    t.name not like 'dt%' 
    and t.is_ms_shipped = 0
    and i.object_id &gt; 255 
group by 
    i.object_id,t.name, s.name, p.rows
order by 
    total_space_mb desc


select * from #t_top_tables
[/sql]

<h2>Step 2: Generate T-SQL to drop all existing indexes and create Clustered Columnstore Indexes(CCI)</h2>
Below T-SQL is ran in same session in generate drop all existing indexes and create CCI statement:
[sql]
declare 
@schema sysname,
@table sysname,
@index sysname,
@sql nvarchar(4000)

declare cur cursor for
select o.schema_name, o.table_name, i.name as index_name from sys.indexes i 
inner join #t_top_tables o on i.object_id=o.object_id
where (i.index_id&gt;0 and i.index_id&lt;255) and i.name not like '_WA_Sys_%'

open cur
fetch next from cur into @schema,@table,@index
while @@fetch_status=0
begin
 set @sql='drop index ['+@schema+'].['+@table+'].['+@index +']'
 select @sql 
 fetch next from cur into @schema,@table,@index
end
close cur
deallocate cur

select 'CREATE CLUSTERED COLUMNSTORE INDEX cl_'+table_name+' ON '+schema_name+'.'+table_name
from #t_top_tables 

drop table  #t_top_tables 
[/sql]

<h2>Step 3: Run the generated script</h2>
Before you run the generated script in production, I recommend you to test in testing environment to measure the duration. Another suggestion is to turn database recovery model to simple to avoid growth of t-log.

<h1>SAS ODBC Error</h1>
Customer hit below error when they testing the SAS code:

<em>ERROR: CLI describe error: [Microsoft][ODBC SQL Server Driver][SQL Server]Cursors are not supported on a table which has a clustered columnstore index. : [Microsoft][ODBC SQL Server Driver][SQL Server]Statement(s) could not be prepared.</em>

The statement is a simple select, but SAS translates it to a cursor. With SAS tech support, we finally find out this is related to a SAS optimization configuration: Block read buffer size. 

Below is how to set the READBUFF=0 in SAS Management Console:

1. Right click the Library in SMC -&gt; Properties -&gt; Options -&gt; Advanced Options – &gt; Optimization -&gt; Block read buffer size 
2. set this property to 0.
<a href="https://support.sas.com/documentation/cdl/en/bidsag/61236/HTML/default/viewer.htm#a002668484.htm">https://support.sas.com/documentation/cdl/en/bidsag/61236/HTML/default/viewer.htm#a002668484.htm</a>

 


Posted by Shiyang, Aug 12 2016
]]></content:encoded>
    </item>
    <item>
      <title>%ErrorLevel% and $LASTEXITCODE</title>
      <link>https://docs.microsoft.com/archive/blogs/shiyangqiu/errorlevel-and-lastexitcode</link>
      <pubDate>Wed, 10 Aug 2016 13:40:35 GMT</pubDate>
      <dc:creator><![CDATA[Shiyang Qiu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/shiyangqiu/?p=235</guid>
      <description><![CDATA[This is the follow-up post of the previous PowerShell and Control-M. There is one scenario I haven't...]]></description>
      <content:encoded><![CDATA[This is the follow-up post of the previous <a href="https://blogs.msdn.microsoft.com/shiyangqiu/2016/08/02/powershell-and-control-m/">PowerShell and Control-M</a>. There is one scenario I haven't discussed: how to get the cmd.exe batch result from PowerShell.

This is not a new question but there is no clear answer you could find in Internet. Here is a post from PowerShell team: <a href="https://blogs.msdn.microsoft.com/powershell/2006/09/15/errorlevel-equivalent/">ErrorLevel equivalent</a>, you could read that $LASTEXITCODE is designed as equivalent to %ErrorLevel% 

<em>In Cmd.exe, %ErrorLevel% is a builtin variable which indicates the success or failure of the last executable run.
$LASTEXITCODE, Contains the exit code of the last Win32 executable execution.</em>

<h1>Problem</h1>

With the cmd.exe batch, the observed result of %ErrorLevel% and $LASTEXITCODE are different in some cases. %ErrorLevel% would not be changed for the "internal" batch command like "GOTO", but $LASTEXITCODE result will be changed. Below is one example, which is a command batch template with error handling in the old days. 

[vb]
@echo off
ping %1
if %errorlevel% NEQ 0 GOTO :Error

echo %1 is reachable.
GOTO :End

:Error
echo Error happens, %1 is not reachable.

:End
[/vb]


If the batch is ran in cmd.exe, 127.0.0.1 and 999.999.999.999 are passed as parameter, expected %ErrorLevel% result 0 and 1 are returned.

 <a href="https://msdnshared.blob.core.windows.net/media/2016/08/20160810_batch_1.png"><img src="https://msdnshared.blob.core.windows.net/media/2016/08/20160810_batch_1.png" alt="20160810_batch_1" width="919" height="511" class="alignnone size-full wp-image-245" /></a>

But the $LASTEXITCODE is always 0 as the ping command is not the last command in the batch file.
<a href="https://msdnshared.blob.core.windows.net/media/2016/08/20160810_batch_2.png"><img src="https://msdnshared.blob.core.windows.net/media/2016/08/20160810_batch_2.png" alt="20160810_batch_2" width="908" height="769" class="alignnone size-full wp-image-255" /></a>

<h1>How to address</h1>
We can add one exit code as the last commend in the batch file to align the $LASTEXITCODE result same as %ErrorLevel%.
Below is the updated version of the sample:
[vb]
REM Sample 2, with exit line
@echo off
ping %1
if %errorlevel% NEQ 0 GOTO :Error

echo %1 is reachable.
GOTO :End

:Error
echo Error happens, %1 is not reachable.

:End
exit /b %errorlevel% 
[/vb]

Now $LASTEXITCODE result is always same as %ErrorLevel%.
<a href="https://msdnshared.blob.core.windows.net/media/2016/08/20160810_batch_3.png"><img src="https://msdnshared.blob.core.windows.net/media/2016/08/20160810_batch_3.png" alt="20160810_batch_3" width="908" height="769" class="alignnone size-full wp-image-265" /></a>

So to the PowerShell + Control-M combination, we will need to add <strong>exit /b %errorlevel% </strong>to the cmd wrapper template for case like PowerShell script recursion.

Posted by Shiyang Qiu, Aug 10, 2016]]></content:encoded>
    </item>
    <item>
      <title>Extract all the connection strings from SSIS packages</title>
      <link>https://docs.microsoft.com/archive/blogs/shiyangqiu/extract-all-the-connection-strings-from-ssis-packages</link>
      <pubDate>Wed, 03 Aug 2016 19:42:24 GMT</pubDate>
      <dc:creator><![CDATA[Shiyang Qiu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/shiyangqiu/?p=205</guid>
      <description><![CDATA[One important task in SSIS upgrade/migration project is the assessment of data provider types,...]]></description>
      <content:encoded><![CDATA[One important task in SSIS upgrade/migration project is the assessment of data provider types, source and destination connection strings in the all packages. The output is used to understand which data source and destination are involved, which data providers are in used. You could use SSDT to check SSIS package but you need a quicker way if the number of package is huge. In my recent project, the number of SSIS package is larger than 600, and below is PowerShell method I use to retrieve the information in 5 min.
<h1>Step 1: Export SSIS packages from msdb</h1>
The first step is to store all SSIS packages in one main folder. I use the PowerShell from Jamie Thomson to export SSIS packages from msdb. Here is the link: <a href="https://sqlblog.com/blogs/jamie_thomson/archive/2011/02/02/export-all-ssis-packages-from-msdb-using-powershell.aspx">Export all SSIS packages from msdb using Powershell</a>
<h1>Step 2: Retrieve the data providers and strings</h1>
Once the packages are stored in the folder, said "C:\ssis_data_provider_check", you could use below PowerShell to dig out information of data providers and connection strings.

[ps]
############################################################
## Retrieve the data providers and strings
##
## Version 0.1
## Created Date 3 Aug, 2016
## Created By Shiyang Qiu (shiyang.qiu@microsoft.com)
############################################################


cls
# $path is the folder of SSIS pacakage
$path = &quot;C:\ssis_data_provider_check&quot;
Push-Location $path

#$all_type_file is the txt file to store the type of data provider
$all_type_file = &quot;$path\all_provider.txt&quot;

#If the file exists, remove it
if(Test-Path $all_type_file) {Remove-Item $all_type_file  -force}

#Findout the all provider types, each data type will be one line in the $all_type_file 
$Lines = dir -I *.dtsx -R | select-string -Pattern &quot;Provider=&quot;
foreach ($L in $Lines)
{
    #RegExp is used to extract the name of data provider 
    $L.Line -match '(Provider=)\w+' | Out-Null; $Matches[0] &gt;&gt; $all_type_file 
}  

#Find out all the data connection strings and store into text file for each data provider.
$all_type_array = get-content .\all_provider.txt | sort|  Get-Unique 

foreach($type in $all_type_array)
{
    dir -I *.dtsx -R | select-string -Pattern $type | Format-Table -auto -Property path, lineNumber, Line | out-string -Width 4096 | out-file $path\$type.txt
}

[/ps]
Here is the result sample:
<a href="https://msdnshared.blob.core.windows.net/media/2016/08/ssis.png"><img width="300" height="226" class="alignnone size-medium wp-image-216" alt="ssis1" src="https://msdnshared.blob.core.windows.net/media/2016/08/ssis-300x226.png" /></a></pre>

<a href="https://msdnshared.blob.core.windows.net/media/2016/08/ssis2.png"><img width="1024" height="433" class="alignnone size-large wp-image-215" alt="ssis2" src="https://msdnshared.blob.core.windows.net/media/2016/08/ssis2-1024x433.png" /></a> 

-- Posted by Shiyang, Aug 3, 2016]]></content:encoded>
    </item>
    <item>
      <title>PowerShell and Control-M</title>
      <link>https://docs.microsoft.com/archive/blogs/shiyangqiu/powershell-and-control-m</link>
      <pubDate>Tue, 02 Aug 2016 16:10:42 GMT</pubDate>
      <dc:creator><![CDATA[Shiyang Qiu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/shiyangqiu/?p=175</guid>
      <description><![CDATA[PowerShell is the best scripting tool for various tasks of Microsoft products, Control-M is one...]]></description>
      <content:encoded><![CDATA[<a href="https://msdn.microsoft.com/en-us/powershell/mt173057.aspx">PowerShell</a> is the best scripting tool for various tasks of Microsoft products, <a href="https://www.bmc.com/it-solutions/control-m.html">Control-M</a> is one popular automation tool in enterprise. But the current Control-M could not call PowerShell script directly.  I work in a customer project recently to migrate their legacy database maintenance jobs from dos batch to PowerShell, which scheduled by Control-M.  Here are my findings and sample code to address the challenges.

PowerShell version I use is PowerShell 5.0. You could use below PowerShell command to check the version in your environment.

[powershell] $PSVersionTable.PSVersion [/powershell]

<h1>Findings and Solutions</h1>
<div>
<table style="border-collapse: collapse" border="0"><colgroup> <col style="width: 623px" /> <col style="width: 623px" /></colgroup>
<tbody valign="top">
<tr>
<td style="padding-left: 14px;padding-right: 14px;border: solid 0.5pt"><strong>Finding</strong></td>
<td style="padding-left: 14px;padding-right: 14px;border-top: solid 0.5pt;border-left: none;border-bottom: solid 0.5pt;border-right: solid 0.5pt"><strong>How to address</strong></td>
</tr>
<tr>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: solid 0.5pt;border-bottom: solid 0.5pt;border-right: solid 0.5pt">Control-M could not call PowerShell directly.</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid 0.5pt;border-right: solid 0.5pt">Cmd batch "wrapper" will be used to call PowerShell script.</td>
</tr>
<tr>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: solid 0.5pt;border-bottom: solid 0.5pt;border-right: solid 0.5pt">Absolute path is needed in the cmd batch wrapper to call the PowerShell script.</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid 0.5pt;border-right: solid 0.5pt">Put the cmd batch wrapper and PowerShell script in same folder, and use <span style="background-color: yellow">%~dp0</span> to set the working folder.</td>
</tr>
<tr>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: solid 0.5pt;border-bottom: solid 0.5pt;border-right: solid 0.5pt">The PowerShell Try-Catch block could not capture all the errors in default setting. There are 2 types of errors in PowerShell, non-terminating errors and terminating errors. Only terminating errors will stop the execution and raise exception. And the Try-Catch block will then catch exception.

Ref: <a href="https://technet.microsoft.com/en-us/magazine/2009.01.windowspowershell.aspx">https://technet.microsoft.com/en-us/magazine/2009.01.windowspowershell.aspx</a></td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid 0.5pt;border-right: solid 0.5pt">Set <span style="background-color: yellow">$ErrorActionPreference</span> to change all non-terminating errors to terminating errors. Use Try-Catch block for the central error handling.</td>
</tr>
<tr>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: solid 0.5pt;border-bottom: solid 0.5pt;border-right: solid 0.5pt">The way to pass back execution result to caller. For PowerShell, there are "<span style="background-color: yellow">return</span>" and "<span style="background-color: yellow">exit</span>" option. The "<span style="background-color: yellow">return</span>" won't stop the execution of PowerShell, the "<span style="background-color: yellow">exit</span>" will. The challenge is how PowerShell script call other PowerShell script or itself as recursion.</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid 0.5pt;border-right: solid 0.5pt">"<span style="background-color: yellow">return</span>" is used for function call, "<span style="background-color: yellow">exit</span>" is used for wrapper call. Inside PowerShell script, the call to other PowerShell script is also through the wrapper.</td>
</tr>
<tr>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: solid 0.5pt;border-bottom: solid 0.5pt;border-right: solid 0.5pt">How to combine with other enterprise monitoring tool like SCOM.</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid 0.5pt;border-right: solid 0.5pt">Define your own error codes and write them to event log.</td>
</tr>
<tr>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: solid 0.5pt;border-bottom: solid 0.5pt;border-right: solid 0.5pt">How to control the debug information output.</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid 0.5pt;border-right: solid 0.5pt">Add a switch parameter, but it has to be int data type as switch type value($true or$false) is not supported by from cmd.</td>
</tr>
</tbody>
</table>
</div>
<h1>Sample Code</h1>
<h2>Batch Wrapper</h2>

[vb]
@echo off
setlocal
REM **********************************************************************************
REM * Name : control_m_demo.bat
REM * For  : Wrapper of control_m_demo.ps1
REM * By   : Shiyang Qiu (shiyang.qiu@microsoft.com)
REM **********************************************************************************

REM * There are 2 parameters, if any parameter is missing go to help
if &quot;%1&quot; == &quot;&quot; goto :HELP
if &quot;%2&quot; == &quot;&quot; goto :HELP

REM * Set the absolute path
SET SET_FOLDER=%~dp0
pushd &quot;%SET_FOLDER=E%&quot;

REM * control_m_demo.ps1 and control_m_demo.bat are in same folder
REM * %3 parameter is a hidden switch to control the output of debuggig info
powershell -file control_m_demo.ps1 %1 %2 %3

goto :END

:HELP

echo.
echo  Command usage: use control_m_demo [para1] [para2] to check if para1/para2 &lt; 1
echo.
echo         [para1] = parameter 1
echo         [para2] = parameter 2
echo.

:END

popd
endlocal

exit /b %errorlevel%

[/vb]

<h2>PowerShell Script</h2>

[ps]
############################################################
##  File Name: control_m_demo.ps1
##
## Version 0.1
## Created Date 2 Aug, 2016
## Created By Shiyang Qiu (shiyang.qiu@microsoft.com)
############################################################


param
(
    [Parameter(Mandatory=$true, Position=1)]
    [int]$Para1,

    [Parameter(Mandatory=$true, Position=2)]
    [int]$Para2,


    #Modify the switch to 0 when deploying the script. 1 is for dev and t-shooting
    [Parameter(Mandatory=$false, Position=3)]
    [ValidateSet(0,1)]
    [int]$_Test = 1
)


## Change the default error action to &quot;Stop&quot;
$ErrorActionPreference = &quot;stop&quot;

## Change the working folder
Push-Location $PSScriptRoot

### Function PrintTestInfo is used to print debug info to screen
function PrintTestInfo($S)
{
    write-host &quot;$(get-date -Format yyyy-MM-dd_HH:mm:ss) Debug output: $S&quot; -ForegroundColor Yellow
}

### Function IfDivResultLessThan1 is to calc para1/para2
function IfDivResultLessThan1
{
    ## when para2 is 0, it will trigger error
    $Result = $Para1/$Para2

    ## return 1 when result &lt; 1 
    ## return 0 when result &gt;=1
    ## error will be captured by outsdie try-catch block
    if ($Result -lt 1) {return 1}
    else {return 0}
}


########## Begin of Main Logic  ######################

Try
{
    if($_Test) {PrintTestInfo(&quot;begin the demo&quot;)}
    if(IfDivResultLessThan1 -eq 1){PrintTestInfo(&quot;$para1/$para2 &lt; 1&quot;)} else {PrintTestInfo(&quot;$para1/$para2 &gt;= 1&quot;)}
    
    ## Exit 0 to wrapper when there is no error
    if($_Test) {PrintTestInfo(&quot;without error&quot;)}
    exit 0

}
catch
{       


        if($_Test) 
        {
            $ErrorMsg = $Error[0].ToString()
            $ErrorInvocationInfo = $Error[0].InvocationInfo.line
            
            PrintTestInfo(&quot;Error info: $ErrorMsg.&quot;)
            PrintTestInfo(&quot;Command: $ErrorInvocationInfo.&quot;)
        }
	## Exit 1 to wrapper when there is error
        exit 1
}


########## End of Main Logic  ######################
[/ps]

The result

<a href="https://msdnshared.blob.core.windows.net/media/2016/08/result.png"><img width="600" height="334" class="alignnone size-medium wp-image-176" alt="result" src="https://msdnshared.blob.core.windows.net/media/2016/08/result.png" /></a>

HTH, Shiyang

Special thanks to David Ho for brain storming this solution.

---------
Aug 10 Update: https://blogs.msdn.microsoft.com/shiyangqiu/2016/08/10/errorlevel-and-lastexitcode/]]></content:encoded>
    </item>
  </channel>
</rss>