I have a table with 500k rows where the address is in one field, delimited by Char(13)+Char(10). I have added 5 fields to the table in the hope of splitting this up.
Found online this split function that seems to perform well as I cannot use parsename due to there being 5 parts and also that the . may be in the field.
This is a table-valued function so I would have to loop the rows and update the record, previously I would have used a cursor or sql while or possibly even c# to do this but I feel their must be a cte or set based answer to do this.
So given some source data:
CREATE TABLE dbo.Addresses
(
AddressID INT IDENTITY(1,1),
[Address] VARCHAR(255),
Address1 VARCHAR(255),
Address2 VARCHAR(255),
Address3 VARCHAR(255),
Address4 VARCHAR(255),
Address5 VARCHAR(255)
);
INSERT dbo.Addresses([Address])
SELECT 'foo
bar'
UNION ALL SELECT 'add1
add2
add3
add4
add5';
Let's create a function that returns the address parts in a sequence:
CREATE FUNCTION dbo.SplitAddressOrdered
(
#AddressID INT,
#List VARCHAR(MAX),
#Delimiter VARCHAR(32)
)
RETURNS TABLE
AS
RETURN
(
SELECT
AddressID = #AddressID,
rn = ROW_NUMBER() OVER (ORDER BY Number),
AddressItem = Item
FROM (SELECT Number, Item = LTRIM(RTRIM(SUBSTRING(#List, Number,
CHARINDEX(#Delimiter, #List + #Delimiter, Number) - Number)))
FROM (SELECT ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects) AS n(Number)
WHERE Number <= CONVERT(INT, LEN(#List))
AND SUBSTRING(#Delimiter + #List, Number, LEN(#Delimiter)) = #Delimiter
) AS y
);
GO
Now you can do this (you will have to run the query 5 times):
DECLARE
#i INT = 1,
#sql NVARCHAR(MAX),
#src NVARCHAR(MAX) = N';WITH x AS
(
SELECT a.*, Original = s.AddressID, s.rn, s.AddressItem
FROM dbo.Addresses AS a
CROSS APPLY dbo.SplitAddressOrdered(a.AddressID, a.Address,
CHAR(13) + CHAR(10)) AS s WHERE rn = #i
)';
WHILE #i <= 5
BEGIN
SET #sql = #src + N'UPDATE x SET Address' + RTRIM(#i)
+ ' = CASE WHEN AddressID = Original AND rn = '
+ RTRIM(#i) + ' THEN AddressItem END;';
EXEC sp_executesql #sql, N'#i INT', #i;
SET #i += 1;
END
Then you can drop the Address column:
ALTER TABLE dbo.Addresses DROP COLUMN [Address];
Then the table has:
AddressID Address1 Address2 Address3 Address4 Address5
--------- -------- -------- -------- -------- --------
1 foo bar NULL NULL NULL
2 add1 add2 add3 add4 add5
I'm sure someone more clever than I will show how to utilize that function without having to loop.
I could also envision a slight change to the function that would allow you to simply pull out a certain element... hold please...
EDIT
Here's a scalar function that is more expensive on its own but allows you to make one pass of the table instead of 5:
CREATE FUNCTION dbo.ElementFromOrderedList
(
#List VARCHAR(MAX),
#Delimiter VARCHAR(32),
#Index SMALLINT
)
RETURNS VARCHAR(255)
AS
BEGIN
RETURN
(
SELECT Item
FROM (SELECT rn = ROW_NUMBER() OVER (ORDER BY Number),
Item = LTRIM(RTRIM(SUBSTRING(#List, Number,
CHARINDEX(#Delimiter, #List + #Delimiter, Number) - Number)))
FROM (SELECT ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects) AS n(Number)
WHERE Number <= CONVERT(INT, LEN(#List))
AND SUBSTRING(#Delimiter + #List, Number, LEN(#Delimiter)) = #Delimiter
) AS y WHERE rn = #Index
);
END
GO
Now the update, given the above table above (prior to the update and prior to the drop), is simply:
UPDATE dbo.Addresses
SET Address1 = dbo.ElementFromOrderedList([Address], CHAR(13) + CHAR(10), 1),
Address2 = dbo.ElementFromOrderedList([Address], CHAR(13) + CHAR(10), 2),
Address3 = dbo.ElementFromOrderedList([Address], CHAR(13) + CHAR(10), 3),
Address4 = dbo.ElementFromOrderedList([Address], CHAR(13) + CHAR(10), 4),
Address5 = dbo.ElementFromOrderedList([Address], CHAR(13) + CHAR(10), 5);
You have couple of options:
You can create a temp table and then parse the address into the temp table and then update the original table by joining it to the temp table.
or
You can write your own T-SQL functions and use those functions in your update statement function like follows:
UPDATE myTable
SET address1 = myGetAddress1Function(address),
address2 = myGetAddress2Function(address)....
Related
I am using SQL Adapter in IBM Worklight 6.1 and SQL Server 2008
Before insert the data to the table, I check whether the data is exist in the database or not.
If true, I select one column from the existed row.
Otherwise, I insert into the table and select one column of them.
The problem is when I insert and select the column, the adapter return only the insertion result.
{
"isSuccessful": true,
"updateStatementResult": {
"updateCount": 1
}
}
After I invoke the same query again, it return the result set.
{
"isSuccessful": true,
"resultSet": [
{
"ACCIDENTID": "A01407V00001"
}
]
}
The following is my table schema
CREATE TABLE accident
(
AccidentID varchar(13) NOT NULL,
Date varchar(12),
Time varchar(5),
Location varchar(150),
City varchar (20),
AssClaimNo varchar(17),
LitClaimNo varchar(17),
AssID varchar(9),
LitID varchar(9),
CLicenPlateNumber varchar(7),
PRIMARY KEY (AccidentID)
);
And this is my SQL statement
DECLARE #DATE VARCHAR(12) = '15 Jul 2014';
DECLARE #TIME VARCHAR(6) = '11:13';
DECLARE #ASSID VARCHAR(10) = '20140700a';
IF((SELECT COUNT(A1.ACCIDENTID)
FROM ACCIDENT A1
WHERE A1.DATE = #DATE AND A1.TIME = #TIME AND A1.ASSID = #ASSID) > 0)
BEGIN
SELECT A.ACCIDENTID
FROM ACCIDENT A
WHERE A.DATE = #DATE AND A.TIME = #TIME AND A.ASSID = #ASSID
END;
ELSE
BEGIN
DECLARE #ROWS INTEGER;
DECLARE #PREFIX VARCHAR(5) = '01407'
SET #ROWS = (SELECT COUNT(*)
FROM LITIGANT
WHERE LITID LIKE 'L'+ RIGHT(CAST(#PREFIX AS VARCHAR(5)),4) +'%')
SET #ROWS = #ROWS + 1;
DECLARE #LITID VARCHAR(9) = 'L' + RIGHT(CAST(#PREFIX AS VARCHAR(5)),4) + RIGHT('000'+CAST(#ROWS AS VARCHAR(4)),4)
INSERT INTO LITIGANT (LITID, LNAME, LEMAIL, LTELNUMBER, LDRIVERLICENSE, LCARBRAND, LCARCOLOR, LCARLICENPLATE, LINSNAME, LINSNUMBER)
VALUES(#LITID, 'Sample', 'sample#sample.com', '0839281938', '93810392', 'Volvo', 'Red', 'AB1234', '', '')
SET #ROWS = (SELECT COUNT(*)
FROM ACCIDENT
WHERE ACCIDENTID LIKE 'A'+#PREFIX+'%')
SET #ROWS = #ROWS + 1
DECLARE #ACCID VARCHAR(13) = 'A' + #PREFIX + 'V' + RIGHT('0000'+CAST(#ROWS AS VARCHAR(5)),5)
DECLARE #ACCCLAIM VARCHAR(16) = #ACCID + '-01'
DECLARE #LITCLAIM VARCHAR(16) = #ACCID + '-02'
INSERT INTO ACCIDENT (ACCIDENTID, DATE, TIME, LOCATION, CITY, ASSCLAIMNO, LITCLAIMNO, ASSID, LITID, CLICENPLATENUMBER)
VALUES( #ACCID, #DATE, #TIME, 'Bangkok', 'Bangkok', #ACCCLAIM, #LITCLAIM, #ASSID, #LITID, 'AR1234')
INSERT INTO CLAIM_STATUS
(CLAIMID, STATUS, ACCIDENTID)
VALUES(#ACCCLAIM, 0, #ACCID)
INSERT INTO CLAIM_STATUS
(CLAIMID, STATUS, ACCIDENTID)
VALUES(#LITCLAIM, 0, #ACCID)
SELECT #ACCID AS ACCIDENTID
END;
Is there a way to get the resultSet when insert into the table ?
Thank you for all suggestions and solutions.
You can get result set of inserted record by following format:
DECLARE #YourTableVariable TABLE(Column1 INT, Column2 INT)
INSERT INTO YourTable( Column1, Column2 )
OUTPUT Inserted.Column1, Inserted.Column2 INTO #YourTableVaribale
VALUES ( #Column1, #Column2)
Select * From #YourTableVariable
I have a query that selects from multiple tables using a join. I want to execute this query from different databases via a loop.
I have accomplished that via (simplified query):
DECLARE #intCounter int
SET #intCounter = 1
DECLARE #tblBedrijven TABLE (ID int identity(1,1),
CompanyName varchar(20),
DatabaseTable varchar(100))
INSERT INTO #tblBedrijven VALUES ('001-CureCare', '<TABLE/ DATABASE1> AUS'),
('002-Cleaning', '[global_nav5_prod].[dbo].<TABLE/ DATABASE2>] AUS')
DECLARE #strCompany varchar(20)
DECLARE #strTable varchar(100)
WHILE (#intCounter <= (SELECT MAX(ID) FROM #tblBedrijven))
BEGIN
SET #strTable = (SELECT DatabaseTable FROM #tblBedrijven
WHERE ID = #intCounter)
SET #strCompany = (SELECT CompanyName FROM #tblBedrijven
WHERE ID = #intCounter)
EXEC('SELECT ''' + #strCompany + ''' as Company,
AUS.[User],
AUS.[E-mail]
FROM' + #strTable)
SET #intCounter = #intCounter + 1
END
My problem is that the result generates 2 separate tables (for every loop). I want to union the results but have no clue how.
Any suggestions?
Thanks in advance.
Can't you use something like the below code where you append all the sqls with union and finally execute the sql once only without executing in a loop. I am not an expert in SQL Server but I have written many other similar stored procedures using other RDBMS. So please bear any syntax errors.
DECLARE #intCounter int
DECLARE #maxId int
SET #intCounter = 1
DECLARE #tblBedrijven TABLE (ID int identity(1,1),
CompanyName varchar(20),
DatabaseTable varchar(100))
INSERT INTO #tblBedrijven VALUES ('001-CureCare', '<TABLE/ DATABASE1> AUS'),
('002-Cleaning', '[global_nav5_prod].[dbo].<TABLE/ DATABASE2>] AUS')
DECLARE #strCompany varchar(20)
DECLARE #strTable varchar(100)
DECLARE #strSql varchar(5000)
SET #maxId = (SELECT MAX(ID) FROM #tblBedrijven)
WHILE (#intCounter <= #maxId)
BEGIN
SET #strTable = (SELECT DatabaseTable FROM #tblBedrijven
WHERE ID = #intCounter)
SET #strCompany = (SELECT CompanyName FROM #tblBedrijven
WHERE ID = #intCounter)
SET #strSql = #strSql + ('SELECT ''' + #strCompany + ''' as Company,
AUS.[User],
AUS.[E-mail]
FROM' + #strTable)
IF #intCounter < #maxId THEN
BEGIN
SET #strSql = #strSql + ' UNION '
END
SET #intCounter = #intCounter + 1
END
EXEC(#strSql)
I need the column alias to be named based on a scenario
declare #testing as varchar(max)
set #testing = 'choice'
select 1 as case when #testing = 'choice' then 'chose' else 'didntChoose' end
So if #testing = 'choice', the results would look like this:
chose
1
else:
didntChoose
1
Is it possible to do this without dynamic SQL?
No, you cannot change the name of the alias based on the value unless you use dynamic SQL.
When you are selecting the columns, you can only have one name/alias for each column.
If you want different column names, then you could use some like the following which uses different select statements:
IF #testing = 'choice'
select 1 as 'Chose'
ELSE
select 1 as 'didntChoose'
Or you could return two separate columns:
select
case when #testing = 'choice' then 1 else 0 end Chose,
case when #testing <> 'choice' then 1 else 0 end DidNotChose
Here is something I wrote that kind of achieves the goal, but it is not the most elegant piece of work I have ever done.
Various customers want to display different values for attributes associated with their resources. So a generic table exists that lets each customer assign different values to each of the resources.
Let's create the structures first and populate them. Nothing too fancy:
create table CustResource (
CustId int,
Attr1 varchar(50),
Attr2 varchar(50),
Attr3 varchar(50),
Attr4 varchar(50),
Attr5 varchar(50))
insert into CustResource (CustId, attr1, attr2, attr3, attr4) values (1, 'Div','Dept','Machine Type','Main Usage')
/* What just happened above is that the customer assigned display values to the first 4 attributes only */
create table PortalResource (
ResourceId int,
custId int,
ResourceName varchar(50),
Attr1 varchar(50),
Attr2 varchar(50),
Attr3 varchar(50),
Attr4 varchar(50),
Attr5 varchar(50))
insert into PortalResource (ResourceId, CustId, ResourceName, attr1, attr2, attr3, attr4)
values (10,1,'abcd1234','Local Government','State Emergency Services','File Server','Production')
insert into PortalResource (ResourceId, CustId, ResourceName, attr1, attr2, attr3, attr4)
values (11,1,'bcde2345','Local Government','State Emergency Services','Database Server','Production')
insert into PortalResource (ResourceId, CustId, ResourceName, attr1, attr2, attr3, attr4)
values (12,1,'bcde2346','Local Government','Department of Education','Domain Controller','Production')
/* Notice in the above that attr5 is not populated. This is deliberate! */
/* OK, now we want to accept the customer Id (I have hard-coded it here for quick reference, but you get the point) */
declare #SQLString varchar(1000)
, #attr1 varchar (50)
, #attr2 varchar(50)
, #attr3 varchar(50)
, #attr4 varchar(50)
, #attr5 varchar(50)
, #CustId varchar(10)
set #CustId = 1
select #attr1 = upper(attr1)
, #attr2 = upper(attr2)
, #attr3 = upper(attr3)
, #attr4 = upper(attr4 )
, #attr5 = UPPER(attr5)
, #CustId = convert(varchar,custId)
from CustResource where custid = #CustId
set #SQLString = 'Select ' + #CustId + 'as CustomerID'
If #attr1 is not null set #SQLString = #SQLString +
' , attr1 as ' + '"' + #attr1 + '"'
If #attr2 is not null set #SQLString = #SQLString +
' , attr2 as ' + '"' + #attr2 + '"'
If #attr3 is not null set #SQLString = #SQLString +
' , attr3 as ' + '"' + #attr3 + '"'
If #attr4 is not null set #SQLString = #SQLString +
' , attr4 as ' + '"' + #attr4 + '"'
If #attr5 is not null set #SQLString = #SQLString +
' , attr5 as ' + '"' + #attr5 + '"'
Set #SQLString = #SQLString + ' from PortalResource where CustId = ' + #CustId
print #SQLString
exec (#SQLString)
This works a charm, but it is super-ugleeeeee!!!!
I'll just leave this here http://www.sommarskog.se/dynamic_sql.html#columnalias
You first get the data into a temp table, and then you use sp_rename
to rename the column along your needs. (You need to qualify sp_rename
with tempdb to have it to operate in that database.)
Do have a read of his site if you're dealing a lot with dynamic SQL, there's a lot of ways to shoot yourself in the foot if you're not careful...
I am using SSMS 2008 and I have the following scalar function to take a text string and remove all metatags from Microsoft Word. The tags are enclosed in "<...>" and there can be any number of tags / record in one column.
I created this scalar function to update each row in this column.
create function dbo.ufn_StripHTML
( #Input varchar(max),
#Delimiter char(1)
)
returns varchar(max)
as
begin
declare #Output varchar(max)
select #Input = replace(replace(#input, '<', #Delimiter), '>', #Delimiter)
select #Output = isnull(#Output, '') + s
from ( select row_number() over (order by n.id asc) [i],
substring(#Delimiter + #Input + #Delimiter, n.id + 1, charindex(#Delimiter, #Delimiter + #Input + #Delimiter, n.id + 1) - n.id - 1) [s]
from [evolv_cs].[dbo].[progress_note] n
where n.id = charindex(#Delimiter, #Delimiter + #Input + #Delimiter, n.id) and
n.id <= len(#Delimiter + #Input)
) d
where i % 2 = 1
return #Output
end
This scalar function would work if [progress_note] had an "id" int column. But it does not and I cannot modify this table either, by adding an int column. So the problem is that I am trying to use this function on a temp table.
So I tried creating a view based on this table and then adding a PK int column to it. Because when I tried to create the view with this additional PK int column ("id"), it gave me an error:
Msg 156, Level 15, State 1, Line 1
Incorrect syntax near the keyword 'identity'.
But ALTER VIEW does not support adding columns. Is there another way to do this? Here is my original temp table I am trying to modify:
select [progress_note].[note_text], [progress_note].[event_log_id]
INTO #TEMP_PN
from [evolv_cs].[dbo].[progress_note]
group by [progress_note].[event_log_id], [progress_note].[note_text]
[note_text] is varchar(max) and event_log_id is uniqueidentifier. So [note_text] contains a bunch of "<" and ">" chars. How can I modify this function to make it a table function?
Of course, if I try to replace [progress_note] table with #TEMP_PN in this function, it will error because it won't recognize it. So how can I modify this function for my case?
Meanwhile, I developed a table function which accepts and outputs a table parameter. It does not error, but it does not return the parsed data I was hoping for either. What is missing?
CREATE TYPE dbo.MyTableType AS TABLE
(
col1 int identity(1,1) NOT NULL,
col2 varchar(max) NULL
)
GO
CREATE TABLE [dbo].[MyTable] (
[col1] [int] identity(1,1) NOT NULL,
[col2] [varchar](max) NULL
)
GO
create PROC usp_AddRowsToMyTable #MyTableParam MyTableType READONLY, #Delimiter varchar(30)
as
INSERT INTO MyTable([col2])
SELECT [col2]
FROM #MyTableParam
--update MyTable
--set col2 = replace(replace(MyTable.col2, '<', #Delimiter), '>', #Delimiter)
select s, i, t
from(
select MyTableInput.col1 [i],
replace(replace(MyTable.col2, '<', #Delimiter), '>', #Delimiter) as t,
substring(#Delimiter + MyTableInput.col2 + #Delimiter, MyTable.col1 + 1,
charindex(#Delimiter, #Delimiter + MyTableInput.col2 + #Delimiter, MyTable.col1 + 1) - MyTable.col1 - 1) [s]
from MyTable
inner join MyTable as MyTableInput on MyTable.col1 = MyTableInput.col1
where MyTable.col1 = CHARINDEX(#Delimiter, #Delimiter + MyTableInput.col2 + #Delimiter, MyTable.col1)
and MyTable.col1 <= LEN(#Delimiter + MyTableInput.col2)
) d
DECLARE #MyTable MyTableType
INSERT INTO #MyTable(col2)
VALUES ('<h><dsf>2000<h><dsf>'),
('<sd><dsf>2001'),
('2002<vnv><dsf>'),
('<gsd><dsf>2003<h><dsf>'),
('<eefs><dsf><h><dsf>2004<dfgd><dsf>')
EXEC dbo.usp_AddRowsToMyTable #MyTableParam = #MyTable, #Delimiter = '|'
Not sure I'm understanding your question, but here is how you would modify your function so that it returns a table (it's called a Table-Valued Function):
create function dbo.ufn_StripHTML
( #Input varchar(max),
#Delimiter char(1)
)
returns #retYourNewTable table
(
id int primary key clustered not null,
yoursecond column varchar(100) null,
....
)
as
....
Is this what you're looking for?
I'm trying to prepare some data for deletion by a 3rd party, and unfortunately they can only process data in batches of 2000 records. I have 100k records and may need to divide-and-export this data several more times, so I'd like to automate the process somehow.
Is there a reasonably easy way to do this using SQL Server 2008? I'm not running a complex query -- it's not too far off from SELECT PKID FROM Sometable ORDER BY PKID -- and while I can probably do this using a cursor, I'd like to know if there's a better way.
SET NOCOUNT ON;
CREATE TABLE [dbo].[SyncAudit] ( PkId INT, BatchNumber INT)
DECLARE #batchsize INT
,#rowcount INT
,#batchcount INT
,#rootdir VARCHAR(2048)
,#saveas VARCHAR(2048)
,#query VARCHAR(2048)
,#bcpquery VARCHAR(2048)
,#bcpconn VARCHAR(64)
,#bcpdelim VARCHAR(2)
SET #rootdir = '\\SERVER1\SHARE1\FOLDER\'
SET #batchsize = 2000
SET #bcpdelim = '|'
SET #bcpconn = '-T' -- Trusted
--SET #bcpconn = '-U <username> -P <password>' -- SQL authentication
SELECT #rowcount = COUNT(1),
#batchcount = CEILING(COUNT(1)/#batchsize) FROM <#TableName, string, 'SomeTable'>
SELECT [BatchSize] = #BatchSize, [BatchCount] = #Batchcount
INSERT INTO SyncAudit
SELECT
<#TableKey, string, 'PKField'>
,groupnum = NTILE(#batchcount) OVER ( ORDER BY <#TableKey, string, 'PKField'>)
FROM
<#TableName, string, 'SomeTable'>
WHILE (#batchcount > 0)
BEGIN
SET #saveas = #rootdir + 'batchnumber-' + cast(#batchcount as varchar) + '.txt'
SET #query = ' SELECT [<#TableName, string, 'SomeTable'>].*
FROM [' + db_name() + '].[dbo].[<#TableName, string, 'SomeTable'>]
JOIN [' + db_name() + '].[dbo].[SyncAudit]
ON [<#TableName, string, 'SomeTable'>].<#TableKey, string, 'PKField'> = [SyncAudit].PkId
AND [SyncAudit].BatchNumber = ' + cast(#batchcount as varchar) + ''
SET #bcpquery = 'bcp "' + replace(#query, char(10), '') + '" QUERYOUT "' + #saveas + '" -c -t^' + #bcpdelim + ' ' + #bcpconn + ' -S ' + ##servername
EXEC master..xp_cmdshell #bcpquery
--EXEC (#query)
SET #batchcount = #batchcount -1
END
DROP TABLE [dbo].[SyncAudit] -- or leave for reference
I think you can take advantage of using ROW_NUMBER and then using BETWEEN to specify a range of rows that you like. Alternatively you could use PKID if you knew there wasn't gaps, or didn't care about the gaps
e.g.
SELECT ...
FROM
(SELECT ...
ROW_NUMBER() OVER(ORDER BY PKID ) as RowNum
FROM Sometable e
) t
WHERE RowNum BETWEEN #startRowIndex AND (#startRowIndex + #maximumRows) - 1
This is often used for paging results. 4GuysFromRolla have a good article on it
You could work out the ranges in a while ##ROWCOUNT loop to target the rows required. It may work better than ROW_NUMBER() which would have to keep numbering from the start.
declare #startid int
declare #endid int
-- get one range, these are efficient as they go over the PKID key by range
select top(1) #startid = pkid from sometable order by pkid -- 1 key visited
select top(2000) #endid = pkid from sometable order by pkid -- 2000 keys visited
-- note: top 2000 may end up with the 514th id if that is the last one
while ##ROWCOUNT > 0
begin
insert otherdb.dbo.backupcopy
select * from sometable
where pkid between #startid and #endid
select top(1) #startid = pkid from sometable
WHERE pkid > #endid -- binary locate
order by pkid
select top(2000) #endid = pkid from sometable
WHERE pkid > #endid -- binary locate, then forward range lookup, max 2000 keys
order by pkid
end
I ended up using a combination of the approaches provided by cyberkiwi and Adam. I didn't need to use ROW_NUMBER only because I used an IDENTITY column in a table data type instead.
Here's a redacted version of the code I used -- it worked like a charm. Thanks again to everyone for all the help!
use Testing
GO
SET NOCOUNT ON
declare
#now datetime = GETDATE(),
#batchsize int = 2000,
#bcpTargetDir varchar(500) = '\\SomeServer\Upload\',
#csvQueryServer varchar(500) = '.\SQLExpress',
#rowcount integer,
#nowstring varchar(100),
#batch_id int,
#startid int,
#endid int,
#oidCSV varchar(max),
#csvQuery varchar(max),
#bcpFilename varchar(200),
#bcpQuery varchar(1000)
declare #tblBatchRanges table (
batch_id integer NOT NULL IDENTITY(1,1) PRIMARY KEY,
oid_start integer NOT NULL,
oid_end integer NOT NULL,
csvQuery varchar(max)
)
-- Create a unique timestamp-based string, which will be used to name the exported files.
select #nowstring = CONVERT(varchar, #now, 112) + '-' + REPLACE(CONVERT(varchar, #now, 114), ':', '')
--
select top(1) #startid = oid from Testing..MyObjectIds order by oid
select top(#batchsize) #endid = oid from Testing..MyObjectIds order by oid
select #rowcount = ##ROWCOUNT
while (#rowcount > 0) begin
-- Create a CSV of all object IDs in the batch, using the STUFF() function
select #csvQuery = 'select stuff((select distinct '','' + CAST(oid as varchar) from Testing..MyObjectIds where oid between ' + CAST(#startid as varchar) + ' and ' + CAST(#endid as varchar) + ' order by '','' + CAST(oid as varchar) for xml path('''')),1,1,'''')'
-- Log the info and get the batch ID.
insert into #tblBatchRanges (oid_start, oid_end, csvQuery)
values (#startid, #endid, #oidCSV, #csvQuery)
select #batch_id = ##IDENTITY
-- Advance #startid and #endid so that they point to the next batch
select top(1) #startid = oid
from Testing..MyObjectIds
where oid > #endid
order by oid
select top(#batchsize) #endid = oid
from Testing..MyObjectIds
where oid > #endid
order by oid
select #rowcount = ##ROWCOUNT
-- Export the current batch to a file.
select #bcpFilename = 'MyExport-' + #nowstring + '-' + cast(#batch_id as varchar) + '.txt'
select #bcpQuery = 'bcp "' + #csvQuery + '" QUERYOUT "' + #bcpTargetDir + #bcpFilename + '" -S ' + #csvQueryServer + ' -T -c'
exec master..xp_cmdshell #bcpquery
end
SET NOCOUNT OFF
--Check all of the logged info.
select oid_start, oid_end, csvQuery from #tblBatchRanges