IF OBJECT_ID('dbo.Numbers1', 'U') IS NOT NULL DROP TABLE dbo.Numbers1; CREATE TABLE dbo.Numbers1 ( NumberID int NOT NULL, INDEX ix_NumberID1 CLUSTERED COLUMNSTORE ); WITH Pass0 as (select 1 as C union all select 1), --2 rows Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows Pass5 as (select 1 as C from Pass4 as A, Pass4 as B),--4,294,967,296 rows Tally as (select row_number() over(order by C) as Number from Pass5) INSERT dbo.Numbers1 (NumberID) select TOP(1000) Number from Tally; ALTER INDEX ix_NumberID1 ON dbo.Numbers1 REORGANIZE; -- Check row groups SELECT b.name,a.partition_number,a.row_group_id,a.state_description,a.total_rows,a.size_in_bytes FROM sys.column_store_row_groups a INNER JOIN sys.objects b ON a.object_id = b.object_id WHERE b.object_id IN (OBJECT_ID('dbo.Numbers1', 'U')) ORDER BY b.name,a.partition_number,a.row_group_id; -- SSMs -- Include Actual Execution Plan -- Batch mode for serial query - NEW! SELECT a.numberID FROM dbo.Numbers1 a INNER JOIN dbo.Numbers1 b ON a.NumberID = b.NumberID;
The server only has 1 CPU so we get a serial plan yet the hash match is run in batch mode
IF OBJECT_ID('dbo.Person', 'U') IS NOT NULL DROP TABLE dbo.Person; CREATE TABLE dbo.Person ( PersonID INT NOT NULL, PersonName NVARCHAR(100), ); ALTER DATABASE justdave SET ALLOW_SNAPSHOT_ISOLATION ON; CREATE CLUSTERED COLUMNSTORE INDEX ix_PersonName ON dbo.Person; SET TRANSACTION ISOLATION LEVEL SNAPSHOT; DBCC USEROPTIONS; BEGIN TRAN; -- Transaction isolation level snapshot - NEW! -- 2014 gives -- Msg 35371, Level 16, State 1, Line 3 -- SNAPSHOT isolation level is not supported on a table which has a clustered columnstore index. INSERT INTO Person(PersonID,PersonName) VALUES (1,'Person 1'); COMMIT TRAN; SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
IF OBJECT_ID('dbo.Numbers1', 'U') IS NOT NULL DROP TABLE dbo.Numbers1; CREATE TABLE dbo.Numbers1 ( NumberID int NOT NULL, INDEX ix_NumberID1 CLUSTERED COLUMNSTORE );
AlwaysOn readable secondary supports updateable columnstore indexes!
There can only be 1 Updatable Non-Clustered Columnstore Index!
IF OBJECT_ID('dbo.Person', 'U') IS NOT NULL DROP TABLE dbo.Person; CREATE TABLE dbo.Person ( PersonID INT NOT NULL, PersonName NVARCHAR(100), ); CREATE CLUSTERED INDEX ix_PersonID ON Person(PersonID); CREATE NONCLUSTERED COLUMNSTORE INDEX ix_PersonName ON Person(PersonName); -- Table is writable - NEW! INSERT INTO dbo.Person VALUES (1,'Person 1');
IF OBJECT_ID('dbo.Person1', 'U') IS NOT NULL DROP TABLE dbo.Person1; CREATE TABLE dbo.Person1 ( PersonID INT NOT NULL, PersonName NVARCHAR(100), ); --Store the table as a columnstore. CREATE CLUSTERED COLUMNSTORE INDEX ix_Person1CCI ON Person1; -- Unique B-tree NC index - NEW CREATE UNIQUE INDEX ix_Person1NC1 ON Person1(PersonID); IF OBJECT_ID('dbo.Person2', 'U') IS NOT NULL DROP TABLE dbo.Person2; CREATE TABLE dbo.Person2 ( PersonID INT NOT NULL, PersonName NVARCHAR(100), ); --Store the table as a columnstore. CREATE CLUSTERED COLUMNSTORE INDEX ix_Person2CCI ON Person2; -- Unique B-tree NC index - NEW CREATE UNIQUE INDEX ix_Person2NC1 ON Person2(PersonID); TRUNCATE TABLE dbo.Person1; INSERT INTO Person1 (PersonID,PersonName) VALUES(1,'Person1'); TRUNCATE TABLE dbo.Person2; INSERT INTO Person2 (PersonID,PersonName) VALUES(1,'First Person'); INSERT INTO Person2 (PersonID,PersonName) VALUES(2,'Second Person'); ALTER INDEX ix_Person1CCI ON dbo.Person1 REORGANIZE; ALTER INDEX ix_Person2CCI ON dbo.Person2 REORGANIZE; SELECT * FROM Person1; SELECT * FROM Person2; # Not supported MERGE Person1 AS T USING Person2 AS S ON (T.PersonID = S.PersonID) WHEN NOT MATCHED BY TARGET THEN INSERT (PersonID,PersonName) VALUES (S.PersonID,S.PersonName) WHEN MATCHED THEN UPDATE SET T.PersonName=S.PersonName;
IF OBJECT_ID('dbo.Person', 'U') IS NOT NULL DROP TABLE dbo.Person; -- NOTE: This must be durability SCHEMA_AND_DATA though CREATE TABLE dbo.Person ( PersonID INT PRIMARY KEY NONCLUSTERED, PersonName NVARCHAR(100), INDEX ix_PersonID CLUSTERED COLUMNSTORE ) WITH (MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_ONLY); -- Primary key still has to be nonclustered -- Therefore memory-optimized tables can be Columnstore+Clustered not B-tree+Clustered -- Non-clustered B-tree index supports foreign keys -- The columnstore index has to be created when the table is created -- The columnstore index cannot be filtered -- The olumnstore index is accessed in interOP mode, not in-memory native mode IF OBJECT_ID('dbo.Person', 'U') IS NOT NULL DROP TABLE dbo.Person; CREATE TABLE dbo.Person ( PersonID INT PRIMARY KEY NONCLUSTERED, PersonName NVARCHAR(100), INDEX ix_PersonID CLUSTERED COLUMNSTORE ) WITH (MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_AND_DATA); -- NOTE: ALTER TABLE is not supported on memory optimized tables that have a column store index. -- Msg 10794, Level 16, State 15, Line 2 -- The operation 'ALTER TABLE' is not supported with memory optimized tables that have a column store index. ALTER TABLE Person ADD PersonComments NVARCHAR(150);
-- Hot and Cold Data IF OBJECT_ID('dbo.Orders', 'U') IS NOT NULL DROP TABLE dbo.Orders; CREATE TABLE Orders ( OrderID int NOT NULL PRIMARY KEY, OrderDate INT, UnitsSold INT ); CREATE NONCLUSTERED COLUMNSTORE INDEX Orders_NCCI1 ON Orders(OrderDate,UnitsSold) WHERE OrderDate < 2016; CREATE NONCLUSTERED INDEX Orders_NCI1 ON Orders(OrderDate) WHERE OrderDate >= 2016; -- Memory optimized + columnstore (or b-tree) + filtered index IF OBJECT_ID('dbo.Orders', 'U') IS NOT NULL DROP TABLE dbo.Orders; CREATE TABLE Orders ( OrderID int NOT NULL PRIMARY KEY NONCLUSTERED, OrderDate INT, UnitsSold INT ) WITH (MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_AND_DATA); -- Msg 10794, Level 16, State 12, Line 1 -- The operation 'CREATE INDEX' is not supported with memory optimized tables. CREATE NONCLUSTERED COLUMNSTORE INDEX Orders_NCCI1 ON Orders(OrderDate,UnitsSold) WHERE OrderDate < 2016; -- Msg 10794, Level 16, State 12, Line 1 -- The operation 'CREATE INDEX' is not supported with memory optimized tables. CREATE NONCLUSTERED INDEX Orders_NCI1 ON Orders(OrderDate) WHERE OrderDate >= 2016;
After the upgrade process read-only nonclustered columnstore index are writeable
Additional logical operators can run in batch mode
IF OBJECT_ID('dbo.Numbers1', 'U') IS NOT NULL DROP TABLE dbo.Numbers1; CREATE TABLE dbo.Numbers1 ( NumberID int NOT NULL, Name varchar(10) ); CREATE CLUSTERED COLUMNSTORE INDEX ix_NumberID1 ON dbo.Numbers1; WITH Pass0 as (select 1 as C union all select 1), --2 rows Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows Pass5 as (select 1 as C from Pass4 as A, Pass4 as B),--4,294,967,296 rows Tally as (select row_number() over(order by C) as Number from Pass5) INSERT dbo.Numbers1 (NumberID) select TOP(10000) Number from Tally; ALTER INDEX ix_NumberID1 ON dbo.Numbers1 REORGANIZE; -- Check row groups SELECT b.name,a.partition_number,a.row_group_id,a.state_description,a.total_rows,a.size_in_bytes FROM sys.column_store_row_groups a INNER JOIN sys.objects b ON a.object_id = b.object_id WHERE b.object_id IN (OBJECT_ID('dbo.Numbers1', 'U')) ORDER BY b.name,a.partition_number,a.row_group_id;
SELECT a.numberID FROM dbo.Numbers1 a INNER JOIN dbo.Numbers1 b ON a.NumberID = b.NumberID;
SELECT a.numberID*2 FROM dbo.Numbers1 a INNER JOIN dbo.Numbers1 b ON a.NumberID = b.NumberID;
SELECT a.numberID FROM dbo.Numbers1 a UNION ALL SELECT a.numberID FROM dbo.Numbers1 a UNION ALL SELECT a.numberID FROM dbo.Numbers1 a;
SELECT a.numberID FROM dbo.Numbers1 a INNER JOIN dbo.Numbers1 b ON a.NumberID = b.NumberID WHERE a.Name is NOT NULL;
SELECT a.numberID FROM dbo.Numbers1 a INNER JOIN dbo.Numbers1 b ON a.NumberID = b.NumberID;
-- Change create index statement to -- change to insert data, create index and do not reorganize as only this order is supported under 2014 CREATE NONCLUSTERED COLUMNSTORE INDEX ix_NumberID1 ON dbo.Numbers1(NumberID); SELECT a.numberID FROM dbo.Numbers1 a WITH (INDEX(ix_NumberID1)) INNER JOIN dbo.Numbers1 b WITH (INDEX(ix_NumberID1)) ON a.NumberID = b.NumberID
Additional operators can run in batch mode - WIP
SELECT STDEV(a.numberID),STDEVP(a.numberID) FROM dbo.Numbers1 a INNER JOIN dbo.Numbers1 b ON a.NumberID = b.NumberID;We have a compute scalar operator running in batch mode
![]()
A hash match aggregate running in batch mode
![]()
Another compute scalar running in batch mode
![]()
Before we finally go to row mode for the stream aggregate and final computer scalar
With SQL Server 2014 we went straight from the hash match join to the row mode stream aggregate
![]()
SELECT SUM(a.numberID),AVG(a.numberID),COUNT(a.numberID),MIN(a.numberID),MAX(a.numberID) FROM dbo.Numbers1 a INNER JOIN dbo.Numbers1 b ON a.NumberID = b.NumberID;We get a hash match aggregate and compute scalar in batch mode before we go to a row mode stream aggregate
Whereas 2014 does not have these operators
SELECT COUNT_BIG(a.numberID),VAR(a.numberID),VARP(a.numberID) FROM dbo.Numbers1 a INNER JOIN dbo.Numbers1 b ON a.NumberID = b.NumberID;
Similarly we get a 2 compute scalars and hash match aggregate all running in match mode
Again 2014 does not have these operators
SELECT CHECKSUM_AGG(CAST(a.numberID as INT)) FROM dbo.Numbers1 a INNER JOIN dbo.Numbers1 b ON a.NumberID = b.NumberID;
IF OBJECT_ID('dbo.Numbers1', 'U') IS NOT NULL DROP TABLE dbo.Numbers1; CREATE TABLE dbo.Numbers1 ( NumberID int NOT NULL, DeptID int NOT NULL, DivisionID int NOT NULL ); CREATE CLUSTERED COLUMNSTORE INDEX ix_NumberID1 ON dbo.Numbers1; WITH Pass0 as (select 1 as C union all select 1), --2 rows Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows Pass5 as (select 1 as C from Pass4 as A, Pass4 as B),--4,294,967,296 rows Tally as (select row_number() over(order by C) as Number from Pass5) INSERT dbo.Numbers1 (NumberID,DeptID,DivisionID) select TOP(10000) Number,Number/10,Number/100 from Tally; ALTER INDEX ix_NumberID1 ON dbo.Numbers1 REORGANIZE; SELECT a.NumberID,a.DeptID,a.DivisionID, CUME_DIST() OVER (PARTITION BY a.DivisionID ORDER BY a.DeptID) AS CumeDist FROM dbo.Numbers1 a INNER JOIN dbo.Numbers1 b ON a.NumberID = b.NumberID;
We get a sort,2 window aggregates and a compute scalar all running in batch mode
Whereas with 2014 we get table spools, segment operators all running in row mode
Only the upper right index scans/hash match and the leftmost computer scalar run in batch mode
SELECT NumberID,DeptID, FIRST_VALUE(NumberID) OVER (ORDER BY DeptID) AS FirstValue FROM dbo.Numbers1;
SELECT DivisionID,DeptID,SUM(NumberID) FROM dbo.Numbers1 GROUP BY GROUPING SETS ((DivisionID), (DeptID))
The query plan has all operators in batch mode
Whereas in 2014 all operators are in row mode
SELECT DivisionID,DeptID,SUM(NumberID),GROUPING_ID(DivisionID) FROM dbo.Numbers1 GROUP BY GROUPING SETS ((DivisionID), (DeptID))
The query plan has all operators in batch mode
Whereas in 2014 all operators are in row mode
SELECT NumberID,DeptID, LAG(NumberID) OVER (ORDER BY DeptID) AS FirstValue FROM dbo.Numbers1; SELECT a.NumberID,a.DeptID,a.DivisionID, LAG(NumberID) OVER (PARTITION BY a.DivisionID ORDER BY a.DeptID) AS CumeDist FROM dbo.Numbers1 a INNER JOIN dbo.Numbers1 b ON a.NumberID = b.NumberID;
SELECT a.NumberID,a.DeptID,a.DivisionID, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY a.DeptID) OVER (PARTITION BY a.DivisionID) AS PercCont, PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY a.DeptID) OVER (PARTITION BY a.DivisionID) AS PercDisc, PERCENT_RANK() OVER (PARTITION BY a.DivisionID ORDER BY a.DeptID ) AS PercRank FROM dbo.Numbers1 a INNER JOIN dbo.Numbers1 b ON a.NumberID = b.NumberID;
The query plan has all operators in batch mode
Whereas in 2014 all operators to the left of the hash match from the index scan (apart from the final computer scalar)are in row mode with spools/nested loop joins
Aggregate computation can be pushed down to be done during the scan node of the query plan
The following operations support pushing the aggregate calculation down to be done during the SCAN node of a query plan
Limitations are:
The aggregation is done on compressed/encoded data in cache-friendly execution and by leveraging SIMD
String predicates can be pushed down in the SCAN node during a query plan
The primary/secondary dictionary created for column compression is used for predicate processing
The predicate is computed against just the dictionary entries rather than each row
If the dictionary entry passes the predicate all rows referring to the dictionary entry are automatically qualified
This results in potential performance improvements:
New DMVs
Updated DMVs
Need to add XEvents - WIP
Newly supported constructs are
AND and EXISTS in the WHERE clause for SELECTs
WHERE is supported with UPDATE and DELETE statements,Subqueries are not supported. In the WHERE or HAVING clause, AND and BETWEEN are supported; OR, NOT, and IN are not supported.
IF OBJECT_ID('dbo.Person', 'U') IS NOT NULL DROP TABLE dbo.Person; CREATE TABLE Person ( PersonID INT NOT NULL, PersonName NVARCHAR(80), PersonQuality NVARCHAR(50), INDEX ix_PersonID NONCLUSTERED HASH (PersonId) WITH (BUCKET_COUNT=2000) ) WITH (MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_ONLY); INSERT into dbo.Person (PersonID) Values(1); IF OBJECT_ID('dbo.PersonUpdateQuality', 'P') IS NOT NULL DROP PROCEDURE dbo.PersonUpdateQuality; CREATE PROCEDURE dbo.PersonUpdateQuality(@PersonID INTEGER) WITH NATIVE_COMPILATION,SCHEMABINDING,EXECUTE AS SELF AS BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english') UPDATE dbo.Person Set PersonQuality='OK' WHERE PersonID=@PersonID; END; SELECT * FROM dbo.Person; EXECUTE dbo.PersonUpdateQuality 1; SELECT * FROM dbo.Person; ALTER PROCEDURE dbo.PersonUpdateQuality(@PersonID INTEGER,@PersonQuality NVARCHAR(50)) WITH NATIVE_COMPILATION,SCHEMABINDING,EXECUTE AS SELF AS BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english') UPDATE dbo.Person Set PersonQuality=@PersonQuality WHERE PersonID=@PersonID; END; SELECT * FROM dbo.Person; EXECUTE dbo.PersonUpdateQuality 1,'Excellent'; SELECT * FROM dbo.Person; -- CTP 2.2 add EXECUTE AS CALLER CREATE PROCEDURE dbo.PersonUpdateQuality(@PersonID INTEGER) WITH NATIVE_COMPILATION,SCHEMABINDING,EXECUTE AS CALLER AS BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english') UPDATE dbo.Person Set PersonQuality='OK' WHERE PersonID=@PersonID; END IF OBJECT_ID('dbo.PersonUpdateQuality', 'P') IS NOT NULL DROP PROCEDURE dbo.PersonUpdateQuality;
IF OBJECT_ID('dbo.Person', 'U') IS NOT NULL DROP TABLE dbo.Person; CREATE TABLE dbo.Person ( PersonID INT NOT NULL, PersonName NVARCHAR(80), PersonQuality NVARCHAR(50) NOT NULL, INDEX ix_PersonID NONCLUSTERED HASH (PersonId) WITH (BUCKET_COUNT=2000) ) WITH (MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_ONLY); INSERT into dbo.Person (PersonID,PersonQuality) Values(1,'First Person'); INSERT into dbo.Person (PersonID,PersonQuality) Values(2,'Second Person'); -- Alter Memory Optimized Table - NEW ALTER TABLE dbo.Person ADD PersonComments NVARCHAR(200) NOT NULL CONSTRAINT PersonOK DEFAULT 'OK' WITH VALUES; ALTER TABLE dbo.Person ADD CONSTRAINT PK_PersonID PRIMARY KEY NONCLUSTERED (PersonID); ALTER TABLE dbo.Person ADD INDEX Quality (PersonQuality); ALTER TABLE dbo.Person DROP INDEX Quality; ALTER TABLE dbo.Person DROP CONSTRAINT PersonOK; ALTER TABLE dbo.Person DROP COLUMN PersonComments; ALTER TABLE dbo.Person ALTER INDEX ix_PersonID REBUILD WITH (BUCKET_COUNT=5000);
In CTP 2.0 In-Memory OLTP now fully supports collations.
-- All syntax needed -- Msg 10796, Level 15, State 2, Procedure fn_securitypredicate, Line 27 -- The SCHEMABINDING option is supported only for natively compiled modules, and is required for those modules. -- Msg 10783, Level 15, State 2, Procedure fn_securitypredicate, Line 19 -- The body of a natively compiled module must be an ATOMIC block. -- Msg 10784, Level 15, State 1, Procedure fn_securitypredicate, Line 18 -- The WITH clause of BEGIN ATOMIC statement must specify a value for the option 'transaction isolation level'. -- Msg 10784, Level 15, State 1, Procedure fn_securitypredicate, Line 18 -- The WITH clause of BEGIN ATOMIC statement must specify a value for the option 'language'. IF OBJECT_ID('dbo.fn_AddOne', 'FN') IS NOT NULL DROP FUNCTION dbo.fn_AddOne; CREATE FUNCTION dbo.fn_AddOne(@Number INTEGER) RETURNS INTEGER WITH NATIVE_COMPILATION,SCHEMABINDING,EXECUTE AS SELF AS BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english') RETURN @Number+1; END; -- CTP 2.2 add EXECUTE AS CALLER IF OBJECT_ID('dbo.fn_AddOne', 'FN') IS NOT NULL DROP FUNCTION dbo.fn_AddOne; CREATE FUNCTION dbo.fn_AddOne(@Number INTEGER) RETURNS INTEGER WITH NATIVE_COMPILATION,SCHEMABINDING,EXECUTE AS CALLER AS BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english') RETURN @Number+1; END;
Constraints on in-memory OLTP tables
IF OBJECT_ID('dbo.Person', 'U') IS NOT NULL DROP TABLE dbo.Person; CREATE TABLE Person ( PersonID INT NOT NULL PRIMARY KEY NONCLUSTERED, PersonName NVARCHAR(80), PersonAge INT CONSTRAINT cc_Page CHECK (PersonAge>0), PersonEmployeeID INT NOT NULL, PersonQuality NVARCHAR(50), INDEX ix_PersonID NONCLUSTERED HASH (PersonId) WITH (BUCKET_COUNT=2000), CONSTRAINT uc_PersonEmployeeID UNIQUE (PersonEmployeeID) ) WITH (MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_ONLY); IF OBJECT_ID('dbo.Orders', 'U') IS NOT NULL DROP TABLE dbo.Orders; CREATE TABLE Orders ( OrderID INT NOT NULL PRIMARY KEY NONCLUSTERED, SalesPerson INT FOREIGN KEY (SalesPerson) REFERENCES dbo.Person(PersonID), UnitsSold INT ) WITH (MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_ONLY);
Triggers on memory optimizerd tables
IF OBJECT_ID('dbo.Orders', 'U') IS NOT NULL DROP TABLE dbo.Orders; CREATE TABLE Orders ( OrderID INT NOT NULL PRIMARY KEY NONCLUSTERED, Department VARCHAR(20), UnitsSold INT ) WITH (MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_ONLY); IF OBJECT_ID('dbo.TotalOrders', 'U') IS NOT NULL DROP TABLE dbo.TotalOrders; CREATE TABLE TotalOrders ( Department VARCHAR(20) PRIMARY KEY NONCLUSTERED, TotalOrders INT NOT NULL, LastAction CHAR(1) ) WITH (MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_ONLY); INSERT INTO dbo.TotalOrders (Department,TotalOrders) VALUES ('Sales',0); IF OBJECT_ID('dbo.OrderInsert', 'TR') IS NOT NULL DROP TRIGGER dbo.OrderInsert; CREATE TRIGGER OrderInsert ON dbo.Orders WITH NATIVE_COMPILATION,SCHEMABINDING AFTER INSERT AS BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english') UPDATE dbo.TotalOrders SET TotalOrders=TotalOrders+1, LastAction='I' WHERE Department='Sales'; END; SELECT * FROM dbo.Orders; SELECT * FROM dbo.TotalOrders; INSERT INTO dbo.Orders(OrderID,Department,UnitsSold) VALUES(1,'Sales',10); INSERT INTO dbo.Orders(OrderID,Department,UnitsSold) VALUES(2,'Sales',20); SELECT * FROM dbo.Orders; SELECT * FROM dbo.TotalOrders; IF OBJECT_ID('dbo.OrderDelete', 'TR') IS NOT NULL DROP TRIGGER dbo.OrderDelete; CREATE TRIGGER OrderDelete ON dbo.Orders WITH NATIVE_COMPILATION,SCHEMABINDING AFTER DELETE AS BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english') UPDATE dbo.TotalOrders SET TotalOrders=TotalOrders-1, LastAction='D' WHERE Department='Sales'; END; SELECT * FROM dbo.Orders; SELECT * FROM dbo.TotalOrders; DELETE FROM dbo.Orders where OrderID=1; SELECT * FROM dbo.Orders; SELECT * FROM dbo.TotalOrders; IF OBJECT_ID('dbo.OrderUpdate', 'TR') IS NOT NULL DROP TRIGGER dbo.OrderUpdate; CREATE TRIGGER OrderUpdate ON dbo.Orders WITH NATIVE_COMPILATION,SCHEMABINDING AFTER UPDATE AS BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english') UPDATE dbo.TotalOrders SET LastAction='U' WHERE Department='Sales'; END; SELECT * FROM dbo.Orders; SELECT * FROM dbo.TotalOrders; UPDATE dbo.Orders SET UnitsSold = 5 WHERE OrderID=2; SELECT * FROM dbo.Orders; SELECT * FROM dbo.TotalOrders;
In CTP 3.1
IF OBJECT_ID('dbo.Person', 'U') IS NOT NULL DROP TABLE dbo.Person; CREATE TABLE Person ( PersonID INT NOT NULL PRIMARY KEY NONCLUSTERED, PersonName NVARCHAR(80), PersonAge INT CHECK (PersonAge>0), PersonEmployeeID INT NULL, -- Nullable index column PersonQuality1 VARCHAR(MAX), PersonQuality2 NVARCHAR(MAX), PersonQuality3 VARBINARY(MAX), INDEX ix_PersonID NONCLUSTERED HASH (PersonId) WITH (BUCKET_COUNT=2000), INDEX ix_PersonEmployeeID UNIQUE (PersonEmployeeID) ) WITH (MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_ONLY);
A database can now have up to 2 terabytes of user data in memory-optimized tables with SCHEMA_AND_DATA
The Save-SqlMigrationReport cmdlet is a tool that evaluates the migration fitness of multiple objects in a SQL Server database.
Currently, it is limited to evaluating the migration fitness for In-Memory OLTP.
Save-SqlMigrationReport [ -MigrationType OLTP ] [ -Server server -Database database [ -Object object_name ] ] | [ -InputObject smo_object ] -FolderPath path
The report goes into 2 folders under FolderPath (Tables and Stored Procedures)
In CTP 2.0, the storage for memory-optimized tables will be encrypted as part of enabling TDE on the database.