Showing posts with label Sql Server. Show all posts
Showing posts with label Sql Server. Show all posts

Thursday, November 26, 2020

[SQL Server] Partition table

 SQL Server   Partition table   

 

▌Introduction


 

This is a simple tutorial for creating partitions on an existing table in Microsoft SQL Server.

 

▌Environment


 

▋Microsoft SQL Server 2017 Express

 

 

▌Steps


 

▋Create a demo table

 

Lets create a table first. We will use [CreateOn] as the the value for partitioning.

 


CREATE TABLE [dbo].[OnlineTxs]
(
    [Id]       [numeric](18, 0) IDENTITY (1,1) NOT NULL,
    [CardNo]   [varchar](19)                   NOT NULL,
    [Amt]      [decimal](10, 0)                NOT NULL,
    [CreateOn] [datetime]                      NOT NULL,
    CONSTRAINT [PK_OnlineTxs] PRIMARY KEY NONCLUSTERED
    (
        [Id] ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
)

 


▋ Create Partition Function and Scheme 


Use Date as the partition boundary of the Partition Function.


CREATE PARTITION FUNCTION pf_date_range (datetime)
AS RANGE RIGHT
FOR VALUES ('2020/11/23','2020/11/24')
-- FOR VALUES (CONVERT(VARCHAR(10), dateadd(day,-1, GETDATE()), 111)) -- Yesterday
GO

CREATE PARTITION SCHEME ps_date_range
    AS PARTITION pf_date_range ALL TO ([PRIMARY])
GO


 

Verify the partitions we just made by the following SQL.


SELECT ps.name,pf.name,boundary_id,value
FROM sys.partition_schemes ps
INNER JOIN sys.partition_functions pf ON pf.function_id=ps.function_id
INNER JOIN sys.partition_range_values prf ON pf.function_id=prf.function_id

 



 

The records will be stored in this way,

Partition number

Range

1

< 2020-11-23 00:00:00

2

>= 2020-11-23 00:00:00 & < 2020-11-24 00:00:00

3

>= 2020-11-24 00:00:00

 

 

 

 

▋ Create the none-clustered constraint and create clustered index

 

Furthermore, we have to recreate the constraint, the primary key must

•     Includes the Partition Boundary column: [CreateOn]

•     None-clustered

•     Be added on the Partition Function

 

And create a clustered index for the Partition Boundary column: [CreateOn].


ALTER TABLE [dbo].[OnlineTxs] DROP CONSTRAINT [PK_OnlineTxs]
GO

ALTER TABLE [dbo].[OnlineTxs]
    ADD CONSTRAINT [PK_OnlineTxs] PRIMARY KEY NONCLUSTERED ([Id] ASC, [CreateOn])
    ON ps_date_range([CreateOn]);
GO

-- Create cluster index
CREATE CLUSTERED INDEX [IX_OnlineTxs_createon] ON [dbo].[OnlineTxs] ([CreateOn])
WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON ps_date_range([CreateOn])
GO

 

 

▋Test the partition table with data

 

We can use the following SQL to generate some data into the table.


CREATE FUNCTION fn_get_random (@GUID uniqueidentifier, @CEILING_VALUE INT)
RETURNS INT
AS
BEGIN
  DECLARE @RAND INT;
  SELECT @RAND = ABS(CHECKSUM(@GUID)) % @CEILING_VALUE;
  RETURN @RAND;
END

-- Create mock data
DECLARE @seq int = 0
WHILE @seq < 1000
BEGIN
    SET @seq = @seq + 1

    INSERT INTO [dbo].[OnlineTxs]([Cardno],[Amt],[CreateOn])
    SELECT '123456****789' AS [CardNo], dbo.fn_get_random(NEWID(), 9999) AS [Amt], DATEADD(day, -1 * dbo.fn_get_random(NEWID(),4), GETDATE()) AS [CreateOn]
END


 

Check the data by summarizing by Partition Number,


SELECT o.name AS [ObjectName],i.name AS [IndexName], partition_id AS [PartitionId], partition_number AS [PartitionNumber], [rows]
FROM sys.partitions p
INNER JOIN sys.objects o ON o.object_id=p.object_id
INNER JOIN sys.indexes i ON i.object_id=p.object_id and p.index_id=i.index_id
WHERE o.name LIKE '%OnlineTxs%'
ORDER BY [IndexName], [PartitionNumber]

 


 

Or list/count the records by Partition Number,


DECLARE @partition_number INT = 1;
SELECT COUNT(*) AS Cnt FROM [dbo].[OnlineTxs]
WHERE $PARTITION.pf_date_range([CreateOn]) = @partition_number;


 

 

▋Move on more actions

 

▋How to truncate by Partition

Truncating by Partition is much faster than doing deletion. Here is how to truncate rows in millionseconds by Partition Number.

 


DECLARE @partition_numer INT = 1;
TRUNCATE TABLE [dbo].[OnlineTxs] WITH (PARTITIONS(@partition_number));

 

 

▋How to create new Partition

Alter the Partition Function to have more Partitions,


ALTER PARTITION SCHEME ps_date_range NEXT USED [PRIMARY];
ALTER PARTITION FUNCTION pf_date_range() SPLIT RANGE ('2020/11/25');


▋How to remove the Partition


ALTER PARTITION FUNCTION pf_date_range() MERGE RANGE(CONVERT(DATETIME, '2020-11-23'));


Notice that the parameter for RANGE() must be DATETIME.

 


▋Remove Partition Scheme and Function 


DROP PARTITION SCHEME ps_date_range;
DROP PARTITION FUNCTION pf_date_range;

 

▌Reference


 

▋How to Partition an existing SQL Server Table

 


Friday, December 8, 2017

[Sql Server] Execute the SP/Query from a Linked server

 Sql Server    Linked Server



▌Environment


▋Windows Server 2012 Standard
▋MS Sql Server 2012



▌Implement


▋SQL

EXEC sp_addLinkedServer
@server = 'MyLinkedSrvName', --Linked Server Name
@srvproduct = '',
@provider=N'SQLNCLI',
@provstr=N'PROVIDER=SQLOLEDB;SERVER=TargetDbSrv',
@location = NULL
GO

EXEC sp_addlinkedsrvlogin
       @rmtsrvname=' MyLinkedSrvName',
       @useself='false',
       @rmtuser='TargetDb_UserId',
       @rmtpassword='TargetDb_UserPwd'
GO


▋Settings

Before we connect to the linked server, notice that

The user of local DB Server must be in sysadmin group

OR

Authorize the user of local DB Server to have the permission when creating the linked server.

EXEC sp_addLinkedServer
@server = 'MyLinkedSrvName', --Linked Server Name
@srvproduct = '',
@provider=N'SQLNCLI',
@provstr=N'PROVIDER=SQLOLEDB;SERVER=TargetDbSrv;User ID=MyLocalUserId',
@location = NULL
GO

EXEC sp_addlinkedsrvlogin
       @rmtsrvname=' MyLinkedSrvName',
       @useself='false',
       @rmtuser='TargetDb_UserId',
       @rmtpassword='TargetDb_UserPwd'
GO




▋Usage

▋Store Prcedure

select * from OPENQUERY
 (MyLinkedSrvName, 'EXEC [DB_Instance_Name].[dbo].[usp_StoreProcedure]')


▋Sql query

SELECT * FROM [MyLinkedSrvName].[ DB_Instance_Name].[dbo].[RemoteTable]




▌Reference



Tuesday, September 19, 2017

[Sql Server] Identify blocking sessions

 Sql Server


以下是如何找出blocking session的方式。



▋dbo.syslockinfo : 找出blocking session

declare @tablename sysname
select @tablename = 'XXXXX' --放被lock住的Table name

select convert (smallint, req_spid) As spid,
rsc_dbid As dbid,
rsc_objid As ObjId,
rsc_indid As IndId,
substring (v.name, 1, 4) As Type,
substring (rsc_text, 1, 32) as Resource,
substring (u.name, 1, 8) As Mode,
substring (x.name, 1, 5) As Status
from master.dbo.syslockinfo,
master.dbo.spt_values v,
master.dbo.spt_values x,
master.dbo.spt_values u
where master.dbo.syslockinfo.rsc_type = v.number
and v.type = 'LR'
and master.dbo.syslockinfo.req_status = x.number
and x.type = 'LS'
and master.dbo.syslockinfo.req_mode + 1 = u.number
and u.type = 'L'
and rsc_objid = object_id(@tablename)



Example:





▋dm_os_waiting_tasks & sys.dm_exec_sessions : 列出該session詳細資訊

USE [master]
GO
SELECT   w.session_id
 ,w.wait_duration_ms
 ,w.wait_type
 ,w.blocking_session_id
 ,w.resource_description
 ,s.program_name
 ,t.text
 ,t.dbid
 ,s.cpu_time
 ,s.memory_usage
FROM sys.dm_os_waiting_tasks w
INNER JOIN sys.dm_exec_sessions s
ON w.session_id = s.session_id
INNER JOIN sys.dm_exec_requests r
ON s.session_id = r.session_id
OUTER APPLY sys.dm_exec_sql_text (r.sql_handle) t
WHERE s.is_user_process = 1
AND w.session_id=451
GO


Example:

從text可以看到session的TSQL。




▋Kill session

KILL { session ID | UOW } [ WITH STATUSONLY ]  


Reference: Microsoft DOC


▋查看活動監視器或Sql profiler







▌Reference



Thursday, December 22, 2016

[Sql Server] Recursive CTE for hierarchy data

 T-SQL   CTE   Hierarchy



▌Problem


Assume that we have a table which include hierarchy data like this, 

OrgSeq

Name

TopOrgSeq

001

Agile dev team

003

002

DevOps team

003

003

Tiger department

008

008

IT

NULL

 

and we would like to know the Agile dev team’s parent hierarchy:

Agile dev team / Tiger department / IT

Here is a sample for using CTE(Common table expression) to solve the problem.





▌Solution


▋CTE

DECLARE @id VARCHAR(20);
SET @id = '001'

;WITH cte_recursive AS (
    SELECT *
    FROM Departments
    WHERE Id=@id
    UNION ALL
    SELECT t.*
    FROM Departments t INNER JOIN
         cte_recursive r ON t.Id = r.Parent
)
SELECT * FROM cte_recursive





 

The CTE results in:




▋Function (Optional)


We can create a function for advanced usage.


CREATE FUNCTION find_hirearchy_departmemts
(
    @id VARCHAR(20)
)
RETURNS TABLE
AS RETURN
    WITH cte_recursive AS (
        SELECT *
        FROM departments
        WHERE id=@id
        UNION ALL
        SELECT t.*
        FROM departments t INNER JOIN
        cte_recursive r ON t.id = r.parent
)
SELECT  * FROM cte_recursive