I was reading the MSDN site here:
http://technet.microsoft.com/en-us/library/bb510625.aspx
and I'm a little confused about one thing.
The example from the site (copied at the bottom) uses the line WHEN NOT MATCHED BY TARGET. My question is, can I use that to do inserts, updates, and deletes all in the same merge? For example:
--Lazy syntax, but I think you get the idea.
MERGE x AS TARGET USING (ID, [More Fields...]) AS SOURCE
WHEN MATCHED
update
WHEN NOT MATCHED IN TARGET
insert
WHEN NOT MATCHED IN SOURCE
delete
MSDN EXAMPLE:
USE AdventureWorks2012;
GO
-- Create a temporary table variable to hold the output actions.
DECLARE #SummaryOfChanges TABLE(Change VARCHAR(20));
MERGE INTO Sales.SalesReason AS Target
USING (VALUES ('Recommendation','Other'), ('Review', 'Marketing'),
('Internet', 'Promotion'))
AS Source (NewName, NewReasonType)
ON Target.Name = Source.NewName
WHEN MATCHED THEN
UPDATE SET ReasonType = Source.NewReasonType
WHEN NOT MATCHED BY TARGET THEN
INSERT (Name, ReasonType) VALUES (NewName, NewReasonType)
OUTPUT $action INTO #SummaryOfChanges;
-- Query the results of the table variable.
SELECT Change, COUNT(*) AS CountPerChange
FROM #SummaryOfChanges
GROUP BY Change;
If you're doing a wholesale swap of the table, then one way would be to create two additional schemas:
CREATE SCHEMA shadow AUTHORIZATION dbo;
CREATE SCHEMA cache AUTHORIZATION dbo;
Now create a copy of your table in the cache schema:
CREATE TABLE cache.SalesReason(Name ...);
Now when you are doing your switch operation:
TRUNCATE TABLE cache.SalesReason;
INSERT cache.SalesReason(Name ...) SELECT ... FROM source;
-- this is a metadata operation so extremely fast - it will wait
-- for existing locks to be released, but won't block new locks
-- for very long at all:
BEGIN TRANSACTION;
ALTER SCHEMA shadow TRANSFER Sales.SalesReason;
ALTER SCHEMA Sales TRANSFER cache.SalesReason;
COMMIT TRANSACTION;
ALTER SCHEMA cache TRANSFER shadow.SalesReason;
TRUNCATE TABLE cache.SalesReason;
-- truncate is optional - I usually kept the data around for debugging
This won't work if you have foreign keys and other dependencies, and of course it completely invalidates statistics etc. and this, in turn, can affect plans, but if the most important thing is getting accurate data in front of your users with minimal interruption, this can be an approach to consider.
Related
Is there an easy way to copy the data of a table to the same database with different table name without logging.
CREATE TABLE SCHEMA.NEW_TB LIKE SCHEMA.OLD_TB;
INSERT INTO SCHEMA.NEW_TB (SELECT * FROM SCHEMA.OLD_TB);
The above 2 statements will work but the table contains huge amount of data. So is it possible to copy the data without logging?
Use the following with auto commit switched off in your session:
CREATE TABLE SCHEMA.NEW_TB LIKE SCHEMA.OLD_TB;
COMMIT;
ALTER TABLE SCHEMA.NEW_TB ACTIVATE NOT LOGGED INITIALLY;
INSERT INTO SCHEMA.NEW_TB
SELECT * FROM SCHEMA.OLD_TB;
COMMIT;
It’s important to use ALTER TABLE and INSERT in the same transaction.
I'm currently writing a stored procedure to update an entire table or on attribute level.
I look if its supposed to update entire table or not. If yes then update entire, if not then look on attribute level whether it's supposed to be a new row, update row, or delete row.
To my help I have three tables: one that says if whole table is to be updated, a new/update table, and a delete table. They consist of data telling what table has changed and when the change occured as well a GUID.
Any ideas on how to write this? Preferred would also be dynamic code but that is not a must. For instance something like:
IF EXISTS (SELECT * from DB1.Schema1.Table1 where UpdateAll = 1) --update all rows, all attributes
BEGIN
'truncate/drop table? update whole table from another table from DB2?'
END
IF EXISTS (SELECT * from DB1.Schema1.Table1 where UpdateAll = 0) --Only on attribute level
BEGIN
'Update table in DB1 based on Delete, or new/update table. And this step
is on attribute level (not update all attributes as step above)'
END
First try to explore the MERGE statement in SQL server: here
If it doesn't full fill your requirement then go with dynamic query execution using sp_executesql: here
Is there any possibility to disable auto creating statistics on specific table in database, without disabling auto creating statistics for entire database?
I have a procedure wich written as follow
create proc
as
create table #someTempTable(many columns, more than 100)
inserting into #someTempTable **always one or two row**
exec proc1
exec proc2
etc.
proc1, proc2 .. coontains many selects and updates like this:
select ..
from #someTempTable t
join someOrdinaryTable t2 on ...
update #someTempTable set col1 = somevalue
Profiler shows that before each select server starts collecting stats in #someTempTable, and it takes more than quarter of entire execution of proc. Proc is using in OLPT processing and should works very fast. I want to change this temporary table to table variable(because for table variables server doesn't collect stats) but can't because it lead me to rewrite all this procedures to passing variables between them and all of this legacy code should be retests. I'm searching alternative way how to force server to behave temporary table like table variables in part of collecting stats.
P.S. I'm know that stats is useful thing but in this case it's useless because table alway contains small amount of records.
I assume you know what you are doing. Disabling a statistics is generally a bad idea. Anyhow:
EXEC sp_autostats 'table_name', 'OFF'
More documentation here: https://msdn.microsoft.com/en-us/library/ms188775.aspx.
Edit: OP clarified that he wants to disable statistics for a temp table. Try this:
CREATE TABLE #someTempTable
(
ID int PRIMARY KEY WITH (STATISTICS_NORECOMPUTE = ON),
...other columns...
)
If you don't have a primary key already, use an identity column for a PK.
I need to merge data from 2 tables into a third (all having the same schema) and generate a mapping of old identity values to new ones. The obvious approach is to loop through the source tables using a cursor, inserting the old and new identity values along the way. Is there a better (possibly set-oriented) way to do this?
UPDATE: One additional bit of info: the destination table already has data.
Create your mapping table with an IDENTITY column for the new ID. Insert from your source tables into this table, creating your mapping.
SET IDENTITY_INSERT ON for your target table.
Insert into the target table from your source tables joined to the mapping table, then SET IDENTITY_INSERT OFF.
I created a mapping table based on the OUTPUT clause of the MERGE statement. No IDENTITY_INSERT required.
In the example below, there is RecordImportQueue and RecordDataImportQueue, and RecordDataImportQueue.RecordID is a FK to RecordImportQueue.RecordID. The data in these staging tables needs to go to Record and RecordData, and FK must be preserved.
RecordImportQueue to Record is done using a MERGE statement, producing a mapping table from its OUTPUT, and RecordDataImportQueue goes to RecordData using an INSERT from a SELECT of the source table joined to the mapping table.
DECLARE #MappingTable table ([NewRecordID] [bigint],[OldRecordID] [bigint])
MERGE [dbo].[Record] AS target
USING (SELECT [InstanceID]
,RecordID AS RecordID_Original
,[Status]
FROM [RecordImportQueue]
) AS source
ON (target.RecordID = NULL) -- can never match as RecordID is IDENTITY NOT NULL.
WHEN NOT MATCHED THEN
INSERT ([InstanceID],[Status])
VALUES (source.[InstanceID],source.[Status])
OUTPUT inserted.RecordID, source.RecordID_Original INTO #MappingTable;
After that, you can insert the records in a referencing table as folows:
INSERT INTO [dbo].[RecordData]
([InstanceID]
,[RecordID]
,[Status])
SELECT [InstanceID]
,mt.NewRecordID -- the new RecordID from the mappingtable
,[Status]
FROM [dbo].[RecordDataImportQueue] AS rdiq
JOIN #MappingTable AS mt
ON rdiq.RecordID = mt.OldRecordID
Although long after the original post, I hope this can help other people, and I'm curious for any feedback.
I think I would temporarily add an extra column to the new table to hold the old ID. Once your inserts are complete, you can extract the mapping into another table and drop the column.
Is there an easy way to remove an identity from a table in SQL Server 2005?
When I use Management Studio, it generates a script that creates a mirror table without the identity, copies the data, drops the table, then renames the mirror table, etc. This script has 5231 lines in it because this table/column have many FK relations.
I'd feel much more comfortable running a simple alter/drop. Any ideas?
EDIT
I think I'm just going to go with the 5,231 line script from Enterprise Manager. However, I'm going to break it up into smaller parts which I can run and control better. This table "behaves" strange, if you try to delete 1 row (even one you just inserted, which is not in any other FK table), you get this error:
delete MyTable where MyPrimaryKey=1234
Msg 8621, Level 17, State 2, Line 1
The query processor ran out of stack space during query optimization. Please simplify the query.
No doubt, all the FKs. We will halt all access to our application and run in single user mode when we make these schema and related application changes. However, we need this to run fast, and I need an idea of how long it will take. I guess that I'll just have to test, test, test.
If you are on SQL Server 2005 or later, you can do this as a simple metadata change (NB: doesn't require an edition supporting partitioning as I originally stated).
Example code pilfered shamelessly from the workaround by Paul White on this Microsoft Connect Item.
USE tempdb;
GO
-- A table with an identity column
CREATE TABLE dbo.Source
(row_id INTEGER IDENTITY PRIMARY KEY NOT NULL, data SQL_VARIANT NULL);
GO
-- Some sample data
INSERT dbo.Source (data)
VALUES (CONVERT(SQL_VARIANT, 4)),
(CONVERT(SQL_VARIANT, 'X')),
(CONVERT(SQL_VARIANT, {d '2009-11-07'})),
(CONVERT(SQL_VARIANT, N'áéíóú'));
GO
-- Remove the identity property
BEGIN TRY;
-- All or nothing
BEGIN TRANSACTION;
-- A table with the same structure as the one with the identity column,
-- but without the identity property
CREATE TABLE dbo.Destination
(row_id INTEGER PRIMARY KEY NOT NULL, data SQL_VARIANT NULL);
-- Metadata switch
ALTER TABLE dbo.Source SWITCH TO dbo.Destination;
-- Drop the old object, which now contains no data
DROP TABLE dbo.Source;
-- Rename the new object to make it look like the old one
EXECUTE sp_rename N'dbo.Destination', N'Source', 'OBJECT';
-- Success
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
-- Bugger!
IF XACT_STATE() <> 0 ROLLBACK TRANSACTION;
PRINT ERROR_MESSAGE();
END CATCH;
GO
-- Test the the identity property has indeed gone
INSERT dbo.Source (row_id, data)
VALUES (5, CONVERT(SQL_VARIANT, N'This works!'))
SELECT row_id,
data
FROM dbo.Source;
GO
-- Tidy up
DROP TABLE dbo.Source;
I don't believe you can directly drop the IDENTITY part of the column. Your best bet is probably to:
add another non-identity column to the table
copy the identity values to that column
drop the original identity column
rename the new column to replace the original column
If the identity column is part of a key or other constraint, you will need to drop those constraints and re-create them after the above operations are complete.
You could add a column to the table that is not an identity column, copy the data, drop the original column, and rename the new column to the old column and recreate the indexes.
Here is a link that shows an example. Still not a simple alter, but it is certainly better than 5231 lines.