Using SQL Server 2008 i have two tables: [Source] and [Target]
I want to check all columns of Source table and see if they exist in Target table.
If they dont exist then i want to add the columns.
I have created a procedure to fix this. The only thing im struggeling with is how to set the datatypes correctly.
I mean, if it is a varchar field i must specify the length. If its an int field i dont need to and so on. Is there a way to do this without creating seperate handeling of every datatype available?
This is incomplete... you'll have to handle the other datatypes that I forgot (I don't think I forgot any). However I did test it and it works.
So to answer your question, no, you have to handle the datatypes.
DECLARE #MasterTable SYSNAME,
#SlaveTable SYSNAME,
#txtSQL VARCHAR(max)
SELECT #MasterTable = 'orderheader',
#SlaveTable = 'orderheader2'
DECLARE #myTable TABLE
(
txtSQL VARCHAR(MAX)
)
INSERT INTO #myTable
(
[txtSQL]
)
SELECT 'ALTER TABLE [dbo].[' +
#SlaveTable +
'] ADD [' +
a.[name] +
'] [' +
typ.[name] +
']' +
CASE typ.[name]
WHEN 'decimal' THEN '(' + CAST(a.[precision] AS VARCHAR(20)) + ',' + CAST(a.[scale] AS VARCHAR(20)) + ')'
WHEN 'numeric' THEN '(' + CAST(a.[precision] AS VARCHAR(20)) + ',' + CAST(a.[scale] AS VARCHAR(20)) + ')'
WHEN 'varchar' THEN '(' + CAST(a.[max_length] AS VARCHAR(20)) + ')'
WHEN 'char' THEN '(' + CAST(a.[max_length] AS VARCHAR(20)) + ')'
WHEN 'nvarchar' THEN '(' + CAST(a.[max_length] AS VARCHAR(20)) + ')'
WHEN 'nchar' THEN '(' + CAST(a.[max_length] AS VARCHAR(20)) + ')'
WHEN 'binary' THEN '(' + CAST(a.[max_length] AS VARCHAR(20)) + ')'
WHEN 'varbinary' THEN '(' + CAST(a.[max_length] AS VARCHAR(20)) + ')'
ELSE ''
END
FROM (
SELECT col.*
FROM sys.tables tbl
INNER JOIN sys.columns col
ON tbl.[object_id] = col.[object_id]
WHERE tbl.[name] = #MasterTable
) a
LEFT JOIN (
SELECT col.*
FROM sys.tables tbl
INNER JOIN sys.columns col
ON tbl.[object_id] = col.[object_id]
WHERE tbl.[name] = #SlaveTable
) b
ON a.[name] = b.[name]
INNER JOIN sys.types typ
ON a.[system_type_id] = typ.[system_type_id]
WHERE b.name IS NULL
WHILE EXISTS
(
SELECT TOP 1 1
FROM #myTable
)
BEGIN
SELECT TOP 1 #txtSQL = txtSQL FROM #myTable
DELETE FROM #myTable WHERE [txtSQL] = #txtSQL
EXEC (#txtSQL)
END
Is using SQL Compare from Red Gate out of the question?
Related
I am working on SQL Server 2008. I have to create json from two tables, which have a one-to-many relationship. The tables are Customer and Orders.
Each customer may have one or many orders. The json is constructed by first getting data from the customer table and then appending all the purchases they have made.
The following is my query. I have also enclosed the json output from the query. It works and creates valid jsons. The problem is that it's too slow as I am using a cursor to loop through the Customer table. I have managed to avoid cursor to get data from the Orders table by using for xml path. Since I have to handle millions of rows, I have to replace the cursor with some other mechanism.
DECLARE #PaymentType VARCHAR(50),
#Email VARCHAR(100),
#OrderId INT
DECLARE CustomerCursor CURSOR FAST_FORWARD FOR
SELECT TOP 10
PaymentType, Email, OrderId
FROM
CUSTOMER
OPEN CustomerCursor
FETCH NEXT FROM CustomerCursor INTO #PaymentType, #Email, #OrderId
WHILE (##FETCH_STATUS = 0)
BEGIN
DECLARE #customer VARCHAR(MAX)
DECLARE #order VARCHAR(MAX)
DECLARE #customer_with_order VARCHAR(MAX)
-- construct order json
SET #order = '[' + STUFF((SELECT ',{"orderProductID":' + CAST(orderProductID AS VARCHAR) +
',"productType":"' + ProductType + '"' +
',"productName":"' + ProductName + '"' +
',"categoryName":"' + CategoryName + '"' + '}'
FROM ORDERS
WHERE orderid = #OrderId
FOR XML PAT(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 1, '') + ']'
-- construct customer json
SET #customer = '{"email":"' + CASE WHEN #Email IS NULL THEN '' ELSE
#Email END + '"'
+ ',"eventName": "ChristmasSale", "dataFields": {'
+ '"orderId":' + CAST(CASE WHEN #OrderId IS NULL THEN 0 ELSE
#OrderId END AS VARCHAR)
+ ',"paymentType":"' + CASE WHEN #PaymentType IS NULL THEN
'' ELSE #PaymentType END + '"'
+ ',"products": '
-- combine these two
SET #customer_with_order = #customer + #order + '}}'
-- insert into CUSTOMER_ORDER_DATA
INSERT INTO CUSTOMER_ORDER_DATA(email, order_id, orders)
VALUES (#Email, #OrderId, #customer_with_order)
FETCH NEXT FROM CustomerCursor INTO #PaymentType, #Email, #OrderId
END
CLOSE CustomerCursor
DEALLOCATE CustomerCursor
I can't test this, but I suspect you could rewrite the above as a set based method as below (as I have no way of testing this, there is no way I can be certain this'll work, if it doesn't, you may need to troubleshoot it a little):
INSERT INTO CUSTOMER_ORDER_DATA(email, order_id, orders)
SELECT C.Email,
C.orderid,
'{"email":"' + CASE WHEN #Email IS NULL THEN '' ELSE
#Email END + '"'
+ ',"eventName": "ChristmasSale", "dataFields": {'
+ '"orderId":' + CAST(CASE WHEN #OrderId IS NULL THEN 0 ELSE
#OrderId END AS varchar)
+ ',"paymentType":"' + CASE WHEN #PaymentType IS NULL THEN
'' ELSE #PaymentType END + '"'
+ ',"products": ' +
('[' + STUFF((
SELECT
',{"orderProductID":' + CAST(orderProductID AS varchar)
+ ',"productType":"' + ProductType + '"'
+ ',"productName":"' + ProductName + '"'
+ ',"categoryName":"' + CategoryName + '"'
+'}'
FROM ORDERS AS O
WHERE O.orderid = C.orderid
FOR XML PATH(''),TYPE).value('.', 'varchar(max)'), 1, 1, '') + ']')
FROM CUSTOMER AS C
Considering the OP has 5 millions rows, then this would likely be a bit much for one batch. Seperating it into batching of say 10,000 may be better for performance over all. Unfortunately the OP is still using 2008, so they don't have access to the OFFSET clause.
Can anybody help on a problem I am having. For the software i am using it won't accept temp tables unless the table is created and inserted into just like i tried with this.
Before i put in the create table and insert into just the table it works fine but when i try this it says the table already exists.
Can anybody see what i am doing wrong and can advise, it's the variables in my temp tables that is throwing me off.
declare #bt_id varchar(20) = '110';
declare #ms_id varchar(20) = '2';
declare #sqlstr varchar(max) = '';
if object_id('tempdb..#measure_raw') is not null
drop table #measure_raw
CREATE TABLE #measure_raw(
meas_id INT,
column_name VARCHAR (255),
vlu_txt VARCHAR (255),
batch_id INT,
row_num INT,
)
insert into #measure_raw
set #sqlstr = 'select det.meas_id,col.tbl_nm+''.''+col.logged_nm as column_name, det.vlu_txt, det.batch_id, '
+ 'case when (det.row_num is null) then 1 else det.row_num end as row_num '
+ 'into #measure_raw from detail_t det '
+ 'inner join column_t col on col.col_id=det.col_id inner join measure_t rul on rul.meas_id=det.meas_id '
+ 'where det.batch_id = ' + #bt_id + ' '
if #ms_id <> '0'
set #sqlstr = #sqlstr + 'and det.meas_id = ' + #ms_id + ' '
set #sqlstr = #sqlstr + 'order by '
+ 'det.batch_id, det.meas_id, det.row_num, col.tbl_nm, col.logged_nm'
exec(#sqlstr)
I Exec one procedure to generate column and use in SRRS dataset :
here's my SP :
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[CrossTab_MultiLV]
( #Select varchar(2000),
#Pivots1Col varchar(100),
#Summaries varchar(500),
#GroupBy varchar(100),
#OtherCols varchar(1000) = Null)
AS
set nocount on
set ansi_warnings on
declare #Vals varchar(8000);
set #Vals = '';
set #OtherCols= isNull(', ' + #OtherCols,'')
create table #temp (Pivots1 varchar(100))
insert into #temp
exec ('select distinct convert(varchar(100),' + #Pivots1Col + ',101) as Pivots1 FROM (' + #Select + ') A')
select #Vals = #Vals + ', ' +
replace(replace(#Summaries,'(','(CASE WHEN ' + #Pivots1Col + '=''' + Pivots1 + ''' THEN '),')[', ' END) as [' + Pivots1 )
from #Temp
order by Pivots1
drop table #Temp
exec ( 'select ' + #GroupBy + #OtherCols + #Vals +
' from (' + #Select + ') A GROUP BY ' + #GroupBy)
set nocount off
set ansi_warnings on
from sp above I just want to process something and generate field by those SP Produce multiple column , but only show the two first column
:
range TotalAccount CL_Only CL_Only_Have_Rate CL_Only_No_Rate EU_CL EU_CL_Have_Rate EU_CL_No_Rate EU_Only EU_Only_Have_Rate EU_Only_No_Rate
12 3 1 1 0 2 2 0 0 0 0
it'll only show : range TotalAccount column , is there any mistake in my stored procedure ??
I would abandon the dynamic stored procedure design - SSRS does not work with these.
Instead I would present the data with fixed columns and use a Column Group in the SSRS design.
A few minutes ago I was only searching for a simple syntax (SQL server) query that will copy a table Row .
This is usually done from time to time, when working on a ASP.net project, testing data with queries
inside the SQL SERVER management studio . so one of the routine actions is copying a row, altering the required columns to be different from each other, then testing data with queries
So I've encountered - this stored procedure- ,as answer by Dan Atkinson
but adding it to where all non testing purpose are stored lead me to think
is it possible to store them in sorted order so I could Distinguish
'utils' or 'testingPurpose' ones from those used in projects
(default folder inside managment treeview is Programmabilty) could this be another folder too
or this is not an option ?
if not , I thought of Utils. prefix like that (if no other way exist)
dbo.Utils.CopyTableRow
dbo.Utils.OtherRoutineActions ....
Or there's a designated way to achieve what I was thinking of.
this is a first "Util" stored procedure i've made , found it's only solution
prefexing it via Util_
ALTER PROCEDURE [dbo].[Utils_TableRowCopy](
#TableName VARCHAR(50) ,
#RowNumberToCopy INT
)
AS
BEGIN
declare #RowIdentity sysname =
(SELECT name FROM sys.identity_columns WHERE object_id = object_id(#TableName)
)
DECLARE #columns VARCHAR(5000), #query VARCHAR(8000);
SET #query = '' ;
SELECT #columns =
CASE
WHEN #columns IS NULL THEN column_name
ELSE #columns + ',' + column_name
END
FROM INFORMATION_SCHEMA.COLUMNS
WHERE (
TABLE_NAME = LTRIM(RTRIM(#TableName))
AND
column_name <> LTRIM(RTRIM(#RowIdentity))
);
SET #query = 'INSERT INTO ' + #TableName + ' (' + #columns + ') SELECT ' + #columns + ' FROM ' + #TableName + ' WHERE ' + #RowIdentity + ' = ' + CAST(#RowNumberToCopy AS VARCHAR);
--SELECT SCOPE_IDENTITY();
declare #query2 VARCHAR(100) = ' Select Top 1 * FROM '+ #TableName +' Order BY ' + #RowIdentity + ' desc' ;
EXEC (#query);
EXEC (#query2);
END
i would like to alter a stored procedure so i will not suplly the identity column name
INSERT INTO tablName ..... WHERE IDENTITY_Column = 10
can i tell sql server managment studio to just refer to the IDENTITY column so it will find
it by its type which is (at least in my tables a default) autoincremet PK ID type
You haven't really given enough code for us to see what you are trying to do but from the snippet in the question.
WHERE IDENTITY_Column = 10
You can just use
WHERE $IDENTITY = 10
for that (to filter against an identity column without specifying the name).
If you do actually need to lookup the column name then an easier way, avoiding deprecated views is
SELECT name
FROM sys.identity_columns
WHERE object_id = object_id('dbo.YourTable')
found this information by now .
that is the plain and simple version .
declare #tblName sysname = '______'--<== enter a table name
declare #NameOfIDColumn sysname =
(
SELECT Name
FROM syscolumns
WHERE COLUMNPROPERTY( id ,name, 'IsIdentity') = 1 and OBJECT_NAME(id)= #tblName )
select #NameOfIDColumn AS 'result'
you could add this as an option to display last row of a table soretd by its record#
declare #query VARCHAR(100) = 'Select Top 1 * FROM '+ #tblName +' Order BY ' + #IdentColumnName + ' desc' ;
EXEC (#query);
and to play around or even make it as a test page in a .net project
make this one as a stored proc that will outpout a message to a test page .
declare #tblName sysname = '______'--<== enter a table name
declare #IdentColumnName sysname =
(
SELECT Name
FROM syscolumns
WHERE COLUMNPROPERTY( id ,name, 'IsIdentity') = 1 and OBJECT_NAME(id)= #tblName )
declare #result VARCHAR (50) = #tblName + ' Identity Column is ' + #IdentColumnName;
select #result AS 'result'
and with a shorter version of "idntity column search", by Martin Smith
declare #tblName sysname = '______'--<== enter a table name
declare #IdentColumnName sysname =
(SELECT name FROM sys.identity_columns WHERE object_id = object_id(#TableName))
declare #result VARCHAR (50) = #tblName + ' Identity Column is ' + #IdentColumnName;
select #result AS 'result'
this is related to a table copy trick i was trying to pull via stored procedure.
USE [YourDataBaseName]
GO
/****** Object: StoredProcedure [dbo].[Utils_TableRowCopy] Script Date: 10/03/2012 18:26:58 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[Utils_TableRowCopy](
#TableName VARCHAR(50) ,
#RowNumberToCopy INT
)
AS
BEGIN
declare #RowIdentity sysname =
(
SELECT name FROM sys.identity_columns WHERE object_id = object_id(#TableName)
)
DECLARE #columns VARCHAR(5000), #query VARCHAR(8000);
SET #query = '' ;
SELECT #columns =
CASE
WHEN #columns IS NULL THEN column_name
ELSE #columns + ',' + column_name
END
FROM INFORMATION_SCHEMA.COLUMNS
WHERE (
TABLE_NAME = LTRIM(RTRIM(#TableName))
AND
column_name <> LTRIM(RTRIM(#RowIdentity))
);
SET #query = 'INSERT INTO ' + #TableName + ' (' + #columns + ') SELECT ' + #columns + ' FROM ' + #TableName + ' WHERE ' + #RowIdentity + ' = ' + CAST(#RowNumberToCopy AS VARCHAR);
--SELECT SCOPE_IDENTITY();
declare #query2 VARCHAR(100) = ' Select Top 1 * FROM '+ #TableName +' Order BY ' + #RowIdentity + ' desc' ;
EXEC (#query);
EXEC (#query2);
END