Sending email from SSRS repor 2008 - email

I have the below supplier data in SSRS 2008 report.
ID Name Details Email
1 abc d1 rob.b#gmail.com
2 def d2 pat#gmail.com
3 ghi d3 golf#gmail.com
4 dft d4 rob.b#gmail.com
I need to send the relevant details to each email id. For example, in the data above, I need to send the entire record of ID 1 and 4 to rob.b#gmail.com.
Is there any way to send just the relevant details to the emails given in the report. Could someone please advise.

Yes - you can use a Data Driven Subscription to do this in SSRS - if you have the Enterprise version of SQL Server.
If you do not have the Enterprise version, you can still do this with a little bit more effort using a scheduled Stored Procedure to emulate the data driven subscription.
Either way, you would have a report that works with a parameter to identify a single record. Then the separate data driven part would have a different query that gets a list of IDs that you need for the report along with the e-mail address to send it to. The data driven part will then generate a separate report for each of the ID parameter that you created in the report and then send them to the address.
Data Driven Subscription properties:
Unfortunately, both methods are too big of a topic to be covered in an answer.
MSDN - Data Driven Subscriptions
MSSQLTips.com - simulate-reporting-services-data-driven-subscriptions
Here's a SP that I made to generate multiple e-mail:
ALTER PROCEDURE [dbo].[RECORD_REVIEW_GENERATION]
AS
DECLARE #SUBSCRIPTION_ID AS VARCHAR(100) = (SELECT SETTING_VALUE FROM WORKING_STORAGE.dbo.SETTINGS WHERE SETTING_NAME = 'REVIEW_SUBSCRIPTION')
DECLARE #SUBJECT VARCHAR(200)
DECLARE #COMMENT VARCHAR(500)
DECLARE #REVIEW_ID VARCHAR(50)
DECLARE #EXTENSION_SETTINGS XML
DECLARE #PARAMETERS XML
DECLARE #RECORD_ID AS INT
DECLARE #MAX_RECORD_ID AS INT
DECLARE #END_DATE AS DATE = GETDATE() - DATEPART(WEEKDAY, GETDATE()) --PREVIOUS SUNDAY
DECLARE #START_DATE AS DATE = DATEADD(DAY, -6, #END_DATE) --PREVIOUS MONDAY
DECLARE #DATE VARCHAR(500) = CONVERT(VARCHAR(8), #END_DATE, 112)
SELECT #EXTENSION_SETTINGS = ExtensionSettings, #PARAMETERS = [Parameters]
FROM ReportServer.dbo.Subscriptions
WHERE SubscriptionID = #SUBSCRIPTION_ID;
IF OBJECT_ID('tempdb..#REVIEWS') IS NOT NULL DROP TABLE #REVIEWS
CREATE TABLE #REVIEWS(ReviewID VARCHAR(50), ProviderName VARCHAR(250), SiteIDNo VARCHAR(20),
ReviewDate VARCHAR(12), UserName VARCHAR(250), VisitPurpose VARCHAR(250),
NoOfPhysicians INT, NoOfRecords INT, RECORD_ID INT IDENTITY(1,1)
)
INSERT INTO #REVIEWS(ReviewID, ProviderName, SiteIDNo, ReviewDate, UserName, VisitPurpose, NoOfPhysicians, NoOfRecords)
SELECT ReviewID, ProviderName, SiteIDNo, ReviewDate, UserFirstName + ' ' + UserLastName AS UserName, VisitPurpose, NoOfPhysicians, NoOfRecords
FROM KHSSQLODSPRD.QI_SITE_REVIEW.dbo.RecordHeader
WHERE ExportDate BETWEEN #START_DATE AND #END_DATE
ORDER BY ReviewDate
SET #RECORD_ID = 1
SET #MAX_RECORD_ID = (SELECT ISNULL(MAX(RECORD_ID), 0) FROM #REVIEWS)
WHILE #RECORD_ID <= #MAX_RECORD_ID
BEGIN
SELECT #REVIEW_ID = ReviewID,
#SUBJECT = 'Review - ' + ProviderName,
#COMMENT = CAST(#RECORD_ID AS VARCHAR(10)) + ' of ' + CAST(#MAX_RECORD_ID AS VARCHAR(10))
+ ' Review Surveys for the week ending ' + RTRIM(CONVERT(VARCHAR(12), #END_DATE, 110)) + '.'
+ CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10) + ProviderName + ' [' + SiteIDNo + '] - Reviewed ' + RTRIM(CONVERT(VARCHAR(12), ReviewDate, 110))
+ '. Review ID ' + ReviewID
+ CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10) + VisitPurpose + ' visit for ' + CAST(CAST(NoOfPhysicians AS INT) AS VARCHAR(20)) + ' physicians with '
+ CAST(CAST(NoOfRecords AS INT) AS VARCHAR(20)) + ' records by ' + UserName + '.'
FROM #REVIEWS
WHERE RECORD_ID = #RECORD_ID
SET #REVIEW_ID = LOWER(#REVIEW_ID)
--SET EXTENSION OPTIONS
SET #EXTENSION_SETTINGS.modify('replace value of (/ParameterValues/ParameterValue[Name="Subject"]/Value/text())[1] with sql:variable("#SUBJECT")');
SET #EXTENSION_SETTINGS.modify('replace value of (/ParameterValues/ParameterValue[Name="Comment"]/Value/text())[1] with sql:variable("#COMMENT")');
--SET REPORT PARAMETERS
SET #PARAMETERS.modify('replace value of (/ParameterValues/ParameterValue[Name="SITE_ID"]/Value/text())[1] with sql:variable("#REVIEW_ID")');
--UPDATE SUBSCRIPTION PARAMETERS
UPDATE dbo.Subscriptions
SET ExtensionSettings = CAST(#EXTENSION_SETTINGS AS VARCHAR(8000)),
[Parameters] = CAST(#PARAMETERS AS VARCHAR(8000))
WHERE SubscriptionID = #SUBSCRIPTION_ID
EXEC dbo.AddEvent #EventType = 'TimedSubscription', #EventData = #SUBSCRIPTION_ID;
--WAIT WHILE REPORT GENERATES
WAITFOR DELAY '00:00:03.000';
--IF REPORT IS STILL EXECUTING, WAIT SOME MORE
WHILE EXISTS (SELECT TOP 1 'X' FROM dbo.Event WHERE EventData = #SUBSCRIPTION_ID ) OR EXISTS (SELECT TOP 1 'X' FROM dbo.Notifications WHERE SubscriptionID = #SUBSCRIPTION_ID)
BEGIN
WAITFOR DELAY '00:00:01.000';
PRINT 'Waiting for subscription to finish'
END
SET #RECORD_ID = #RECORD_ID + 1
END --END OF LOOP
-- SET #FLAG = 1
You'd add the TO address the same way as the Comment is updated. The #Review_ID is passed to the report as a parameter.

Related

Error converting data type varchar to numeric in standard audit trigger

I have been using a standard block of TSQL for auditing of various tables for some time now. However I now have a problem when running the trigger on a new table: "Error converting data type varchar to numeric". This occurs when running the EXEC (#sql) line. I've determined that the code for #sql is:
insert Audit_AppointmentsWS
(Type,
TableName,
PK,
FieldName,
OldValue,
NewValue,
UpdateDate,
UserName)
SELECT 'U',
'AppointmentsWorkshop',
+convert(varchar(100), coalesce(i.UniqueID,d.UniqueID)),
'[JobHours]',
convert(varchar(1000),d.[JobHours]),
convert(varchar(1000),i.[JobHours]),
'20220816 12:32:43:410',
'DELLXPS\ian'
from #ins i full outer join #del d on i.UniqueID = d.UniqueID where ISNULL(i.JobHours],'') <> ISNULL(d.[JobHours],'')
I've tried deleting the trigger & the audit table and then recreating them but no joy. I've also tried copying an existing trigger and just changing the table details but I still get the same error. I'm completely stumped on this and would appreciate some feedback. Many thanks in advance!
Here is the trigger:
/****** Object: Trigger [dbo].[tr_AppointmentsWS] Script Date: 16/08/2022 12:02:10 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create TRIGGER [dbo].[tr_AppointmentsWS] ON [dbo].AppointmentsWorkshop FOR UPDATE, DELETE
AS
DECLARE #bit INT ,
#field INT ,
#maxfield INT ,
#char INT ,
#fieldname VARCHAR(128) ,
#TableName VARCHAR(128) ,
#AuditTable VARCHAR(128) ,
#PKCols VARCHAR(MAX) ,
#sql VARCHAR(2000),
#UpdateDate VARCHAR(21) ,
#UserName VARCHAR(128) ,
#Type CHAR(1) ,
#PKSelect VARCHAR(MAX)
--Changes required:
-- 1. Change the name of the trigger and the table, above
-- 2. Change #TableName to match the table to be audited
-- 3. Change the #AuditTable to the table holding the changes
SELECT #TableName = 'AppointmentsWorkshop'
SELECT #AuditTable = 'Audit_AppointmentsWS'
-- date and user
SELECT #UserName = SYSTEM_USER ,
#UpdateDate = CONVERT(VARCHAR(8), GETDATE(), 112) + ' ' + CONVERT(VARCHAR(12), GETDATE(), 114)
-- Action
IF EXISTS (SELECT * FROM inserted)
IF EXISTS (SELECT * FROM deleted)
SELECT #Type = 'U'
ELSE
SELECT #Type = 'I'
ELSE
SELECT #Type = 'D'
-- get list of columns
SELECT * INTO #ins FROM inserted
SELECT * INTO #del FROM deleted
-- Get primary key columns for full outer join
SELECT #PKCols = COALESCE(#PKCols + ' and', ' on') + ' i.' + c.COLUMN_NAME + ' = d.' + c.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk, INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
WHERE pk.TABLE_NAME = #TableName
AND CONSTRAINT_TYPE = 'PRIMARY KEY'
AND c.TABLE_NAME = pk.TABLE_NAME
AND c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME
-- Get primary key select for insert
SELECT #PKSelect = COALESCE(#PKSelect+'+','') + '+convert(varchar(100), coalesce(i.' + COLUMN_NAME +',d.' + COLUMN_NAME + '))'
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk, INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
WHERE pk.TABLE_NAME = #TableName
AND CONSTRAINT_TYPE = 'PRIMARY KEY'
AND c.TABLE_NAME = pk.TABLE_NAME
AND c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME
IF #PKCols IS NULL
BEGIN
RAISERROR('no PK on table %s', 16, -1, #TableName)
RETURN
END
SELECT #field = 0, #maxfield = MAX(COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + #Tablename),COLUMN_NAME, 'ColumnID'))
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #TableName
WHILE #field < #maxfield
BEGIN
SELECT #field = MIN(COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + #Tablename),COLUMN_NAME, 'ColumnID'))
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableName
AND COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + #Tablename),COLUMN_NAME, 'ColumnID') > #field
SELECT #bit = (#field - 1 )% 8 + 1
SELECT #bit = POWER(2,#bit - 1)
SELECT #char = ((#field - 1) / 8) + 1
IF SUBSTRING(COLUMNS_UPDATED(),#char, 1) & #bit > 0 OR #Type IN ('I','D')
BEGIN
SELECT #fieldname = COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableName
AND COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + #Tablename),COLUMN_NAME, 'ColumnID') = #field
SELECT #sql = 'insert ' + #AuditTable + '
(Type,
TableName,
PK,
FieldName,
OldValue,
NewValue,
UpdateDate,
UserName)
SELECT ''' + #Type + ''','''
+ #TableName + ''',' + #PKSelect
+ ',''[' + #fieldname + ']'''
+ ',convert(varchar(1000),d.[' + #fieldname + '])'
+ ',convert(varchar(1000),i.[' + #fieldname + '])'
+ ',''' + #UpdateDate + ''''
+ ',''' + #UserName + ''''
+ ' from #ins i full outer join #del d'
+ #PKCols
+ ' where ISNULL(i.[' + #fieldname + '],'''') <> ISNULL(d.[' + #fieldname + '],'''')' --Skip identical values and excludes NULLS vs empty strings
EXEC (#sql)
END
END
Well I finally figured it out. The error is being generated with columns of data type 'decimal' and it is down to the ISNULL section of the last SELECT. I've fixed it by checking for the decimal type and then using the following code (which included a zero rather than an empty sting):
+ ' where ISNULL(i.[' + #fieldname + '],''0'') <> ISNULL(d.[' + #fieldname + '],''0'')' --Skip identical values and excludes NULLS vs empty strings

Executing a trigger with some linked server components

I have a row being inserted into [Server1].[DatabaseA].[dbo].[EMRVisit]. I need to add some information to the Visit record from linked server [Server2].[DatabaseB], so I set up an AFTER INSERT trigger.
Both servers are MSSQL 2016, databases are running at 2008 compatibility at the moment.
Currently, the trigger is blocking insertion of the row. I'm not seeing anything in Profiler, other that the Insert attempt and a rollback. Also, it's not generating any other errors. The code runs fine outside the trigger.
The frustrating part of this, is that the trigger was operating fine as is on older 2008 boxes.
ALTER TRIGGER [dbo].[Pt_Note_Edit]
ON [dbo].[EMRVisit]
AFTER INSERT
AS
IF ((SELECT TRIGGER_NESTLEVEL()) < 2)
BEGIN
DECLARE #med_rec_nbr varchar(15), #Days VARCHAR(20), #OldNote nvarchar(MAX), #NewNote nvarchar(MAX)
SET #med_rec_nbr = (SELECT ept.PatientChartNumber FROM INSERTED ev INNER JOIN [Server1].[DatabaseA].[dbo].[Patients] ept ON ev.PtID = ept.PtID)
--Collect ADS Data
--Convert #med_rec_nbr to #Account
DECLARE #Account varchar(15)
EXEC [Server2].[DatabaseB].[dbo].ADS_CleanUp_MRN #med_rec_nbr, #Account=#Account OUTPUT
SELECT *
INTO #PtChgs
FROM [Server2].[DatabaseB].[dbo].ADS_Charges
WHERE AccountNo = #Account
--Get Old Pt Note
SELECT #OldNote = ept.PatientNote
FROM [Server1].[DatabaseA].[dbo].[Patients] ept
WHERE ept.PatientChartNumber = #med_rec_nbr
--Edit OldNote
DECLARE #NotePart nvarchar(MAX), #Trans Int
SET #NotePart = CASE
WHEN #OldNote IS NOT NULL THEN SUBSTRING(#OldNote,CHARINDEX('~',#OldNote,1)+1,LEN(#OldNote))
ELSE ''
END
--Get Latest Post Op Charges
SELECT Charges.FromDate
,Charges.Modifiers
,DATEDIFF(DAY, Charges.FromDate, GETDATE()) AS DaysIn
INTO #PtPostOp
FROM [Server2].[DatabaseB].[dbo].[Charges] Charges
JOIN [Server2].[DatabaseB].[dbo].[ProcedureCodes] PC ON Charges.Cpt = PC.Code
WHERE Charges.AccountNo = #Account
AND Charges.FromDate > CONVERT(VARCHAR, DATEADD (DAY , -(PC.NumberOfDays + 10) , GETDATE()), 112)
AND PC.NumberOfDays <> 0
ORDER BY Charges.FromDate DESC
DECLARE #FromDate integer,#Modifiers VARCHAR(5), #DaysIn VARCHAR (2), #Enc_Rows int, #Mod varchar(3)
--Get count of encounters in Post Op period for current patient
SET #Enc_Rows = 0
SELECT #Enc_Rows = COUNT(*) FROM #PtPostOp
--Loop through records and concatenate rows
SET #Days = ''
WHILE #Enc_Rows > 0
BEGIN
--Get first record
SELECT TOP 1 #FromDate = FromDate
,#Modifiers = CASE
WHEN Modifiers LIKE '%RT%' THEN 'OD'
WHEN Modifiers LIKE '%LT%' THEN 'OS'
ELSE Modifiers
END
,#DaysIn = DaysIn
FROM #PtPostOp
ORDER BY FromDate DESC
--Concatenate Row
SET #Days = #Days + #Modifiers + ' ' + #DaysIn + ' days'
IF #Enc_Rows > 1
SET #Days = #Days + ': '
SET #Enc_Rows = #Enc_Rows -1
DELETE FROM #PtPostOp WHERE FromDate = #FromDate
END
IF #Days <> ''
SET #NewNote = ('**POST OP ' + #Days + ' ** ~' + #NotePart)
ELSE
SET #NewNote = #NotePart
--Update the PatientNote
UPDATE [Server1].[Database].[dbo].[Patients]
SET PatientNote = #NewNote
WHERE PatientChartNumber = #med_rec_nbr
END
As an addition to this - I'm also attempting to add a try - catch to my code, but that is generating DTC errors.

Query to send mail to mutliple users with their correspoing tickets

I have a table which has a list of ticket owners and and tickets which are assigned to them which are of high priority.
For ex:
enter image description here
I need to send mail from sql server using sp_send_dbmail to John with all the tickets assigned to him in a single mail. This is a huge table so I want send mails to users with their assigned tickets in single mail instead of sending one mail per ticket.
Any help would be appreciated.
A very quick google search would give you all the information you need:
https://msdn.microsoft.com/en-gb/library/ms190307.aspx
This page tells you exactly what you need to know.
This should do the trick...
SET NOCOUNT ON;
--sample of your tickets table
CREATE TABLE #Tickets(
TicketId INT IDENTITY,
TicketOwner VARCHAR(100),
TicketDetails VARCHAR(MAX)
)
;
--some sample values
INSERT INTO #Tickets(
TicketOwner,
TicketDetails
)
VALUES
('John', 'This is the first ticket'),
('John', 'This is the second ticket'),
('Jeremy', 'This is the third ticket')
;
--gets the dense_rank so you can iterate through the list of distinct users
SELECT
TicketId,
TicketOwner,
TicketDetails,
DENSE_RANK() OVER (ORDER BY TicketOwner) AS RowNum
INTO #Temp
FROM #Tickets
ORDER BY TicketOwner, TicketId
;
--holds the details of each email
DECLARE
#RowNum INT = (SELECT MAX(RowNum) FROM #Temp),
#MySubject VARCHAR(100) = '',
#MyBody VARCHAR(MAX) = ''
;
--iterate through distinct TicketOwnders by using the dense rank value above
WHILE #RowNum > 0
BEGIN
--assign variables
SELECT #MySubject = TicketOwner + '''s tickets:',
#MyBody = #MyBody + 'Ticket #' + CAST(TicketId AS VARCHAR(10)) + ': ' + TicketDetails + CHAR(10) + CHAR(13)
FROM #Temp
WHERE RowNum = #RowNum
;
--send mail
EXEC msdb.dbo.sp_send_dbmail
#recipients = 'jgiaco#fanatics.com',
#subject = #MySubject,
#body = #MyBody
;
SET #RowNum = #RowNum - 1;
SET #MyBody = '';
SET #MySubject = '';
END
;

MS SQL Server 2012 Trigger Syntax

I am working on HW assignment learning about triggers and am having problems with syntax on both insert and delete DML ForTriggers as follows:
CREATE TRIGGER tr_PERSON_ForInsert
ON PERSON
FOR INSERT
AS
BEGIN
DECLARE #SSN CHAR(9)
SELECT #SSN = SSN FROM inserted
DECLARE #NAME VARCHAR(50)
SELECT #NAME = NAME FROM inserted
DECLARE #USERNAME VARCHAR(50)
SELECT #USERNAME USERNAME FROM INSERTED
declare #time time(7)
select #time = time from inserted
insert into PERSON_DEL_LOG
VALUES ('New person with SSN = ' +
Cast(#SSN as NVARchar(9)) +
CAST(#NAME AS NVARCHAR(50)) +
cast(#username as nvarchar(50)) +
cast(#time as nvarchar(20))
)
END
INSERT INTO PERSON('012675543', 'MIKE', '5467896543', 'MUSEUM', 'INTEL',
'BLUECROSS', '987654321')
CREATE TRIGGER tr_PERSON_ForDELETE
ON PERSON
FOR DELETE
AS
BEGIN
DECLARE #SSN CHAR(9)
SELECT #SSN = SSN FROM DELETED
DECLARE #NAME VARCHAR(50)
SELECT #NAME = NAME FROM DELETED
insert into PERSON_DEL_LOG
VALUES ('New person with SSN = ' +
Cast(#SSN as nvarchar(9)) +
Cast(#NAME as VARCHAR(50)) + 'is added at ' +
cast(Getdate() as nvarchar(20))
)
END
DELETE FROM PERSON WHERE NAME = 987654321
SELECT * FROM PERSON_DEL_LOG
My PERSON_DEL_LOG table structure:
SSN, NAME, USERNAME, TIME
I need to know where exactly is my problem/s

How to add a column in TSQL after a specific column?

I have a table:
MyTable
ID
FieldA
FieldB
I want to alter the table and add a column so it looks like:
MyTable
ID
NewField
FieldA
FieldB
In MySQL I would so a:
ALTER TABLE MyTable ADD COLUMN NewField int NULL AFTER ID;
One line, nice, simple, works great. How do I do this in Microsoft's world?
Unfortunately you can't.
If you really want them in that order you'll have to create a new table with the columns in that order and copy data. Or rename columns etc. There is no easy way.
solution:
This will work for tables where there are no dependencies on the changing table which would trigger cascading events. First make sure you can drop the table you want to restructure without any disastrous repercussions. Take a note of all the dependencies and column constraints associated with your table (i.e. triggers, indexes, etc.). You may need to put them back in when you are done.
STEP 1: create the temp table to hold all the records from the table you want to restructure. Do not forget to include the new column.
CREATE TABLE #tmp_myTable
( [new_column] [int] NOT NULL, <-- new column has been inserted here!
[idx] [bigint] NOT NULL,
[name] [nvarchar](30) NOT NULL,
[active] [bit] NOT NULL
)
STEP 2: Make sure all records have been copied over and that the column structure looks the way you want.
SELECT TOP 10 * FROM #tmp_myTable ORDER BY 1 DESC
-- you can do COUNT(*) or anything to make sure you copied all the records
STEP 3: DROP the original table:
DROP TABLE myTable
If you are paranoid about bad things could happen, just rename the original table (instead of dropping it). This way it can be always returned back.
EXEC sp_rename myTable, myTable_Copy
STEP 4: Recreate the table myTable the way you want (should match match the #tmp_myTable table structure)
CREATE TABLE myTable
( [new_column] [int] NOT NULL,
[idx] [bigint] NOT NULL,
[name] [nvarchar](30) NOT NULL,
[active] [bit] NOT NULL
)
-- do not forget any constraints you may need
STEP 5: Copy the all the records from the temp #tmp_myTable table into the new (improved) table myTable.
INSERT INTO myTable ([new_column],[idx],[name],[active])
SELECT [new_column],[idx],[name],[active]
FROM #tmp_myTable
STEP 6: Check if all the data is back in your new, improved table myTable. If yes, clean up after yourself and DROP the temp table #tmp_myTable and the myTable_Copy table if you chose to rename it instead of dropping it.
You should be able to do this if you create the column using the GUI in Management Studio. I believe Management studio is actually completely recreating the table, which is why this appears to happen.
As others have mentioned, the order of columns in a table doesn't matter, and if it does there is something wrong with your code.
In SQL Enterprise Management Studio, open up your table, add the column where you want it, and then -- instead of saving the change -- generate the change script. You can see how it's done in SQL.
In short, what others have said is right. SQL Management studio pulls all your data into a temp table, drops the table, recreates it with columns in the right order, and puts the temp table data back in there. There is no simple syntax for adding a column in a specific position.
/*
Script to change the column order of a table
Note this will create a new table to replace the original table.
WARNING : Original Table could be dropped.
HOWEVER it doesn't copy the triggers or other table properties - just the data
*/
Generate a new table with the columns in the order that you require
Select Column2, Column1, Column3 Into NewTable from OldTable
Delete the original table
Drop Table OldTable;
Rename the new table
EXEC sp_rename 'NewTable', 'OldTable';
In Microsoft SQL Server Management Studio (the admin tool for MSSQL) just go into "design" on a table and drag the column to the new position. Not command line but you can do it.
This is absolutely possible. Although you shouldn't do it unless you know what you are dealing with.
Took me about 2 days to figure it out.
Here is a stored procedure where i enter:
---database name
(schema name is "_" for readability)
---table name
---column
---column data type
(column added is always null, otherwise you won't be able to insert)
---the position of the new column.
Since I'm working with tables from SAM toolkit (and some of them have > 80 columns) , the typical variable won't be able to contain the query. That forces the need of external file. Now be careful where you store that file and who has access on NTFS and network level.
Cheers!
USE [master]
GO
/****** Object: StoredProcedure [SP_Set].[TrasferDataAtColumnLevel] Script Date: 8/27/2014 2:59:30 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [SP_Set].[TrasferDataAtColumnLevel]
(
#database varchar(100),
#table varchar(100),
#column varchar(100),
#position int,
#datatype varchar(20)
)
AS
BEGIN
set nocount on
exec ('
declare #oldC varchar(200), #oldCDataType varchar(200), #oldCLen int,#oldCPos int
create table Test ( dummy int)
declare #columns varchar(max) = ''''
declare #columnVars varchar(max) = ''''
declare #columnsDecl varchar(max) = ''''
declare #printVars varchar(max) = ''''
DECLARE MY_CURSOR CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY FOR
select column_name, data_type, character_maximum_length, ORDINAL_POSITION from ' + #database + '.INFORMATION_SCHEMA.COLUMNS where table_name = ''' + #table + '''
OPEN MY_CURSOR FETCH NEXT FROM MY_CURSOR INTO #oldC, #oldCDataType, #oldCLen, #oldCPos WHILE ##FETCH_STATUS = 0 BEGIN
if(#oldCPos = ' + #position + ')
begin
exec(''alter table Test add [' + #column + '] ' + #datatype + ' null'')
end
if(#oldCDataType != ''timestamp'')
begin
set #columns += #oldC + '' , ''
set #columnVars += ''#'' + #oldC + '' , ''
if(#oldCLen is null)
begin
if(#oldCDataType != ''uniqueidentifier'')
begin
set #printVars += '' print convert('' + #oldCDataType + '',#'' + #oldC + '')''
set #columnsDecl += ''#'' + #oldC + '' '' + #oldCDataType + '', ''
exec(''alter table Test add ['' + #oldC + ''] '' + #oldCDataType + '' null'')
end
else
begin
set #printVars += '' print convert(varchar(50),#'' + #oldC + '')''
set #columnsDecl += ''#'' + #oldC + '' '' + #oldCDataType + '', ''
exec(''alter table Test add ['' + #oldC + ''] '' + #oldCDataType + '' null'')
end
end
else
begin
if(#oldCLen < 0)
begin
set #oldCLen = 4000
end
set #printVars += '' print #'' + #oldC
set #columnsDecl += ''#'' + #oldC + '' '' + #oldCDataType + ''('' + convert(character,#oldCLen) + '') , ''
exec(''alter table Test add ['' + #oldC + ''] '' + #oldCDataType + ''('' + #oldCLen + '') null'')
end
end
if exists (select column_name from INFORMATION_SCHEMA.COLUMNS where table_name = ''Test'' and column_name = ''dummy'')
begin
alter table Test drop column dummy
end
FETCH NEXT FROM MY_CURSOR INTO #oldC, #oldCDataType, #oldCLen, #oldCPos END CLOSE MY_CURSOR DEALLOCATE MY_CURSOR
set #columns = reverse(substring(reverse(#columns), charindex('','',reverse(#columns)) +1, len(#columns)))
set #columnVars = reverse(substring(reverse(#columnVars), charindex('','',reverse(#columnVars)) +1, len(#columnVars)))
set #columnsDecl = reverse(substring(reverse(#columnsDecl), charindex('','',reverse(#columnsDecl)) +1, len(#columnsDecl)))
set #columns = replace(replace(REPLACE(#columns, '' '', ''''), char(9) + char(9),'' ''), char(9), '''')
set #columnVars = replace(replace(REPLACE(#columnVars, '' '', ''''), char(9) + char(9),'' ''), char(9), '''')
set #columnsDecl = replace(replace(REPLACE(#columnsDecl, '' '', ''''), char(9) + char(9),'' ''),char(9), '''')
set #printVars = REVERSE(substring(reverse(#printVars), charindex(''+'',reverse(#printVars))+1, len(#printVars)))
create table query (id int identity(1,1), string varchar(max))
insert into query values (''declare '' + #columnsDecl + ''
DECLARE MY_CURSOR CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY FOR '')
insert into query values (''select '' + #columns + '' from ' + #database + '._.' + #table + ''')
insert into query values (''OPEN MY_CURSOR FETCH NEXT FROM MY_CURSOR INTO '' + #columnVars + '' WHILE ##FETCH_STATUS = 0 BEGIN '')
insert into query values (#printVars )
insert into query values ( '' insert into Test ('')
insert into query values (#columns)
insert into query values ( '') values ( '' + #columnVars + '')'')
insert into query values (''FETCH NEXT FROM MY_CURSOR INTO '' + #columnVars + '' END CLOSE MY_CURSOR DEALLOCATE MY_CURSOR'')
declare #path varchar(100) = ''C:\query.sql''
declare #query varchar(500) = ''bcp "select string from query order by id" queryout '' + #path + '' -t, -c -S '' + ##servername + '' -T''
exec master..xp_cmdshell #query
set #query = ''sqlcmd -S '' + ##servername + '' -i '' + #path
EXEC xp_cmdshell #query
set #query = ''del '' + #path
exec xp_cmdshell #query
drop table ' + #database + '._.' + #table + '
select * into ' + #database + '._.' + #table + ' from Test
drop table query
drop table Test ')
END
Even if the question is old, a more accurate answer about Management Studio would be required.
You can create the column manually or with Management Studio. But Management Studio will require to recreate the table and will result in a time out if you have too much data in it already, avoid unless the table is light.
To change the order of the columns you simply need to move them around in Management Studio. This should not require (Exceptions most likely exists) that Management Studio to recreate the table since it most likely change the ordination of the columns in the table definitions.
I've done it this way on numerous occasion with tables that I could not add columns with the GUI because of the data in them. Then moved the columns around with the GUI of Management Studio and simply saved them.
You will go from an assured time out to a few seconds of waiting.
If you are using the GUI to do this you must deselect the following option allowing the table to be dropped,
Create New Add new Column Table Script ex: [DBName].[dbo].[TableName]_NEW
COPY old table data to new table: INSERT INTO newTable ( col1,col2,...) SELECT col1,col2,... FROM oldTable
Check records old and new are the same:
DROP old table
rename newtable to oldtable
rerun your sp add new colum value
-- 1. Create New Add new Column Table Script
CREATE TABLE newTable
( [new_column] [int] NOT NULL, <-- new column has been inserted here!
[idx] [bigint] NOT NULL,
[name] [nvarchar](30) NOT NULL,
[active] [bit] NOT NULL
)
-- 2. COPY old table data to new table:
INSERT INTO newTable ([new_column],[idx],[name],[active])
SELECT [new_column],[idx],[name],[active]
FROM oldTable
-- 3. Check records old and new are the same:
select sum(cnt) FROM (
SELECT 'table_1' AS table_name, COUNT(*) cnt FROM newTable
UNION
SELECT 'table_2' AS table_name, -COUNT(*) cnt FROM oldTable
) AS cnt_sum
-- 4. DROP old table
DROP TABLE oldTable
-- 5. rename newtable to oldtable
USE [DB_NAME]
EXEC sp_rename newTable, oldTable
You have to rebuild the table. Luckily, the order of the columns doesn't matter at all!
Watch as I magically reorder your columns:
SELECT ID, Newfield, FieldA, FieldB FROM MyTable
Also this has been asked about a bazillion times before.