I'm using SQL SERVER 2017 (Developper Edition 64 Bit) on a Windows 10 Machine. I'm trying to execute in a dynamic sql a batch multiples times through GO and it won't work.
But the sql Statement will work if it is not execute dynamically. The goal is to do it dynamically and I still don't figure out what I am doing wrong.
Here is how the definition tables look like:
ParentTable
(
Id uniqueidentifier DEFAULT (newsequentialid()) not null,
Created datetime not null,
Creator uniqueidentifier not null,
Modifier uniqueidentifier null,
Modified datetime null
)
ChildTable
(
Id uniqueidentifier DEFAULT (newsequentialid()) not null,
ParentTable_Id not null,
Created datetime not null,
Creator uniqueidentifier not null,
Modifier uniqueidentifier null,
Modified datetime null
)
This is what I've tried so far:
create Procedure InsertIntoChildTable
AS
BEGIN
DECLARE #countDset int
DECLARE #todaysdate datetime
DECLARE #UserName uniqueidentifier
DECLARE #ParentTable_Id uniqueidentifier
DECLARE #insertIntoChildTable nvarchar(max)
DECLARE #ChildTableName nvarchar(35)
SET #ChildTableName = ChildTable
SET #countDset = 6
SET #todaysdate = GETDATE()
SET #UserName = 'e86aacf4-9887-e911-9724-4439c492b2a7'
BEGIN TRY
BEGIN TRANSACTION
SET #insertIntoChildTable = 'INSERT INTO ' + #ChildTableName + '
(ParentTable_Id, Created, Creator, Modified, Modifier)
VALUES ( (select max(Id) from ParentTable) , #todaysdate, #UserName ,
NULL, NULL) ' + ' GO ' + #countDset
EXECUTE sp_executesql #insertIntoChildTable,N'#ChildTableName
nvarchar(35), #todaysdate datetime, #UserName uniqueidentifier,
#countDset int', #ChildTableName = #ChildTableName, #todaysdate =
#todaysdate, #UserName = #UserName, #countDset = #countDset
COMMIT TRANSACTION
END TRY
BEGIN CATCH
PRINT 'Could not insert in the Child table'
ROLLBACK TRANSACTION
RETURN
END CATCH
END
After the Line with 'Go ' + #countDset it will automatically go into the Catch block and return.
Thank you
INSERT INTO ChildTable ( ParentTable_Id, Created, Creator, Modified, Modifier )
SELECT (select max(Id) from ParentTable) as ParentId , #todaysdate, #UserName ,
NULL, NULL
FROM
(
SELECT TOP #countDset '' As Dummy
FROM sys.Objects As A
CROSS JOIN sys.Objects As B
) As Multiplier
Guided through the example Zohar posted I could insert new Rows in the ChildTable using CROSS JOIN
I have this problem where bulk insert fails with different files sizes, specifically, the first file would contain 433 rows while the second file has 2 rows only. But when I insert the 2 rowed file by itself, there's no error.
I have this as my bulk insert statement
set #sql = 'BULK INSERT #temptable
FROM '''+#location+'''
WITH
(
FIELDTERMINATOR = '','',
ROWTERMINATOR = ''\n'',
MAXERRORS = 100,
TABLOCK,
FIRSTROW = 2
)'
The above statement is contained in a stored procedure.
For more information, I will put my stored procedure here.
USE [THERMOWAVE]
GO
/****** Object: StoredProcedure [dbo].[UploadOmronData] Script Date: 02/13/2017 13:48:54 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[UploadOmronData]
#location as nvarchar(max)
as
begin
declare #sql as nvarchar(max)
-- give #location data from VB.net
--declare #location as varchar(max)
--set #location = 'C:\Users\jsumgo\Documents\Visual Studio 2010\Projects\TestUploader\TestUploader\bin\Debug\12320161428420dataUpload.csv'
-- Joshua Magsino 11/28/2016
-- TSql to upload dynamic column data source
--before uploading, make sure that datasource table columns are named equal to database table column
--Check if data is to be stored as reference or could be destroyed
--Check database to store and columns that would be affected
--set configurations to enable Ad Hoc Distributed Queries
/*
sp_configure 'show advanced options', 1;
RECONFIGURE;
GO
sp_configure 'Ad Hoc Distributed Queries', 1;
RECONFIGURE;
GO
*/
-- configure SQL to execute Ad Hoc Queries using MSOFFICE 2010
/*
USE [master]
GO
EXEC master.dbo.sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0', N'AllowInProcess', 1
GO
EXEC master.dbo.sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0', N'DynamicParameters', 1
GO
*/
-- fill temptable with data
---------------------------------------------------------------------------------------------------------------
--drop table #temptable
--create blank temptable
create table #temptable
(
[Number] Varchar(20)
)
--declare #sql as varchar(max)
declare #cursor CURSOR
declare #colname as nvarchar(30)
SET #cursor = CURSOR FOR
(Select colname from dbo.sorting)
OPEN #cursor
FETCH NEXT
FROM #cursor INTO #colname
WHILE ##FETCH_STATUS = 0
BEGIN
-- add from column 2 to end
if #colname <> 'Number'
begin
set #sql = 'ALTER TABLE #temptable ADD ['+#colName+'] NVARCHAR(max) NULL'
EXEC sp_executesql #sql, N'#colname nvarchar(max)', #colname
end
FETCH NEXT
FROM #cursor INTO #colName
END
CLOSE #cursor
DEALLOCATE #cursor
---------------------------------------------------------------------------------------------------------------
--declare #location as varchar(max)
--set #location = 'D:\Omron Data\Data\sample\asdasdasdasdasd - Copy.csv'
--declare #sql as varchar(max)
--Insert Data from CSV file to Temptable
set #sql = 'BULK INSERT #temptable
FROM '''+#location+'''
WITH
(
FIELDTERMINATOR = '','',
ROWTERMINATOR = ''\n'',
MAXERRORS = 100,
TABLOCK,
FIRSTROW = 2
)'
EXEC sp_executesql #sql, N'#location as nvarchar(max)', #location
--Insert Statement
--If the location of the new column is in the last column of the CSV file, then direct bulk insert
--safe solution is to insert based on the column name of each table
--cursor is needed
DECLARE #DBcursor CURSOR
DECLARE #DBColname as varchar(30)
DECLARE #DBCOL as nvarchar(max)
--Get Table Names from dbo.sorting
--since all table names are inserted from dbo.sorting, get column names from dbo.sorting
SET #DBcursor = CURSOR FOR
(Select colname from dbo.sorting)
OPEN #DBcursor
FETCH NEXT
FROM #DBcursor INTO #DBColname
WHILE ##FETCH_STATUS = 0
BEGIN
--assemble columns
set #DBCOL = (isnull(#DBCOL,''))+'['+ #DBColname +'],'
FETCH NEXT
FROM #DBcursor INTO #DBColname
END
CLOSE #DBcursor
DEALLOCATE #DBcursor
--remove last (,) from columns
set #DBCOL =LEFT(#DBCOL, LEN(#DBCOL) - 1)
--insert data from temptable to DataUpload
set #sql = 'insert into DataToUpload ('+#DBCOL+') select '+#DBCOL+' from #tempTable'
EXEC sp_executesql #sql,N'#DBCOL NVARCHAR(max)', #DBCOL
--Put BatchID for DataUpload
--BatchId format,Batch + Number(000) + year + month + day
declare #number as nvarchar(max)
set #number = RIGHT('000' +CAST((select ISNULL(max(case when BatchID = null or BatchID = '' then '000' else right(SUBSTRING(BatchID,1,8),3)+ 1 end),'000') from DataToUpload)AS VARCHAR(3)),3)
set #sql = 'update DataToUpload set BatchID = ''Batch'+cast(#number as varchar(max))+cast(year(getdate()) as varchar(4))+''+cast(MONTH(getdate())as varchar(2))+''+cast(DAY(getdate())as varchar(2)) +''' where BatchID is null'
EXEC sp_executesql #sql,N'#number NVARCHAR(max)', #number
--select * from DataToUpload order by BAtchID,Number asc
--select * from dbo.Sorting
--select * from temptable
--delete from DataToUpload
--clear dbo.sorting
delete from dbo.Sorting
--remove temptable
drop table #temptable
DBCC FREEPROCCACHE WITH NO_INFOMSGS;
end
and then the command in a vb.net system that fires the stored procedure through here.
Sub UploadOmronData(ByVal location As String)
'SQL Stored Procedure -- Save data from CSV file to SQL
Dim sqlcom As New SqlClient.SqlCommand
Try
With sqlcom
conn.Open()
.Connection = conn
.CommandTimeout = 100
.CommandText = "dbo.UploadOmronData"
.CommandType = CommandType.StoredProcedure
.Parameters.AddWithValue("#location", location)
.ExecuteReader()
End With
Catch ex As Exception
MsgBox(ex.Message + " " + location)
GC.Collect()
sqlcom.Dispose()
frmMain.Close()
End Try
conn.Close()
sqlcom.Dispose()
GC.Collect()
End Sub
And the error message which I receive:
Bulk load data conversion error (truncation) for row 2, column 1 (Number).
Bulk load data conversion error (truncation) for row 3, column 1 (Number).
ADDED INFORMATION
Here is the table structure of dbo.DataToUpload
CREATE TABLE [dbo].[DataToUpload](
[Number] [int] NULL,
[Date&Time] [nvarchar](max) NULL,
[ms] [nvarchar](50) NULL,
[CH34] [nvarchar](50) NULL,
[CH35] [nvarchar](50) NULL,
[CH36] [nvarchar](50) NULL,
[CH37] [nvarchar](50) NULL,
[CH38] [nvarchar](50) NULL,
[CH39] [nvarchar](50) NULL,
[Alarm1-10] [nvarchar](50) NULL,
[Alarm11-20] [nvarchar](50) NULL,
[Alarm21-30] [nvarchar](50) NULL,
[Alarm31-40] [nvarchar](50) NULL,
[AlarmOut] [nvarchar](50) NULL,
[BatchID] [nvarchar](50) NULL,
[CH31] [nvarchar](20) NULL,
[CH32] [nvarchar](20) NULL,
[CH33] [nvarchar](20) NULL
) ON [PRIMARY]
and the rows that I'm trying to insert
Number,Date&Time,ms,CH34,CH35,CH36,CH37,CH38,CH39,Alarm1-10,Alarm11-20,Alarm21-30,Alarm31-40,AlarmOut
1,2016-08-08 16:23:16,000,+61.2,+64.0,+35.4,+94.4,+185.4,+151.2,LLLLLLLLLL,LLLLLLLLLL,LLLLLLLLLL,LLLLLLLLLL,LLLL
2,2016-08-08 16:23:26,000,+61.1,+64.4,+35.4,+94.3,+185.4,+151.2,LLLLLLLLLL,LLLLLLLLLL,LLLLLLLLLL,LLLLLLLLLL,LLLL
and If it mayhelp, the system I created is a background worker, since I have to run the code always.
I reckon the cause is that you're asking BULK INSERT to load 14 columns of data, but you're importing into #TempTable which doesn't have enough fields in it.
Your code creates a #tempTable table, and you use a CURSOR to add extra fields to it... but I reckon the end result isn't a table with 14 fields in it.
Before your BULK INSERT #temptable command gets run, how many fields are in the #temptable table ?
For example, if you just had one field in this table, when you ran the bulk-insert it would insist on trying to import the entire row into that one field, complete with all the commas, but, of course, it doesn't fit in a 20-character string.
CREATE TABLE #temptable
(
[Number] VARCHAR(20)
)
The solution... simply change your temporary table to have the correct number of columns, and the BULK INSERT will work fine:
CREATE TABLE #temptable
(
[Number] NVARCHAR(200),
[Datey] datetime,
[ms] NVARCHAR(200),
[CH34] NVARCHAR(200),
[CH35] NVARCHAR(200),
[CH36] NVARCHAR(200),
[CH37] NVARCHAR(200),
[CH38] NVARCHAR(200),
[CH39] NVARCHAR(200),
[Alarm110] NVARCHAR(200),
[Alarm1120] NVARCHAR(200),
[Alarm2130] NVARCHAR(200),
[Alarm3140] NVARCHAR(200),
[AlarmOut] NVARCHAR(200)
)
Good luck !
P.S. One piece of the puzzle which is missing.... What data do you have in your dbo.sorting table ? This will dictate which fields end up in your temporary table, but you don't explain how this table is populated, or what data it contains.
The attributes
[BatchID] [nvarchar](50) NULL,
[CH31] [nvarchar](20) NULL,
[CH32] [nvarchar](20) NULL,
[CH33] [nvarchar](20) NULL
are missing from your file. With BULK_INSERT the columns have to match perfectly. Fix the number of columns, and it should fix the error.
By developing app. I need a dynamic stored procedure, where I am calling a table name (#pxxx), and I also ned pass same parameters - here I have 2 (datum, por).
It is working well, passing tablename as #pxxx - o.k. Also when parameters datum,por are type -int or varchar, or char or combination of it. Int I change to varchar by CONVERT - procedure go well. Well ig going calling from C#.
Problem is when I change parameter -datum to datetime. It is not working. Changing date by "convert" to varchar not helped. It is not functional in sql management studio. I need to solve a problem, because I need in table datum [datetime],
I can't change table definition, datetime is needed.
I looked for almost every questions about passing parameters and dynamic sql stored procedures, but I not find solution.
Table definition in MS SQL 2005:
CREATE TABLE [dbo].[p001](
[id] [bigint] IDENTITY(1,1) NOT NULL,
[datum] [datetime] NULL,
[por] [int] NULL,
[cas] [nchar](5) NULL,
[max] [int] NULL,
[obsad] [int] NULL,
[blok] [bit] NULL
one of it ) ON [PRIMARY]
My stored procedure - not working
ALTER PROCEDURE [dbo].[SPinsertpxx2] (#pxxx nchar(4),#date datetime, #por int)
AS
DECLARE #sqlCommand varchar(500)
SET #sqlCommand = 'INSERT INTO '+ #pxxx +' ([datum],[por]) VALUES('
+CONVERT(VARCHAR,#date,112)+','+CONVERT(VARCHAR,#por,5)+')'
BEGIN
EXEC (#sqlCommand)
END
This stored procedure - working well but only withous datetime parameter.
ALTER PROCEDURE [dbo].[SPinsertpxxx] (#pxxx nchar(4),#cas nvarchar(10), #por int)
AS
DECLARE #sqlCommand varchar(500)
SET #sqlCommand = 'INSERT INTO '+ #pxxx +' ([cas],[por]) VALUES('
+CONVERT(VARCHAR,#cas)+','+CONVERT(VARCHAR,#por)+')'
BEGIN
EXEC (#sqlCommand)
END
Here is a working example of doing this using parameterzied dynamic sql. This helps prevent sql injection and allows you to use properly typed parameters.
create PROCEDURE [dbo].[SPinsertpxx2]
(
#pxxx nchar(4)
, #datum datetime
, #por int
) AS
DECLARE #sqlCommand nvarchar(max)
SET #sqlCommand = 'INSERT INTO '+ #pxxx +' ([datum],[por]) VALUES(#datum, #por)'
exec sp_executesql #sqlCommand, N'#datum datetime, #por int', #datum = #datum, #por = #por
Now to test this and see if it works.
exec SPinsertpxx2 'p001', '2016-01-01', 42
Does the table contain the correct values?
select * from p001
I have a tree of strings stored in a sql table, this is the definition for the table.
CREATE TABLE [FileTree] (
[ID] INT NOT NULL,
[Name] VARCHAR (MAX) NOT NULL,
[ParentID] INT NULL,
[UserID] VARCHAR (MAX) NOT NULL
);
And I have a recursive delete procedure for deleting a node and it's children, it uses a cursor and it works perfectly well after a user on SO in my previous question regarding this matter pointed out the problems with the syntax. (Deleting Tree-Nodes in a SQL table)
CREATE PROCEDURE DeleteFile
#FileID INTEGER,
#UserID VARCHAR(MAX)
AS
BEGIN
DELETE FROM [FileTree] WHERE [ID] = #FileID AND [UserID]=#UserID;
IF EXISTS(SELECT * FROM [FileTree] WHERE [ParentID] = #FileID AND [UserID]=#UserID)
BEGIN
DECLARE FileCursor CURSOR LOCAL FOR
SELECT [ID],[UserID] FROM [FileTree] WHERE [ParentID] = #FileID AND [UserID]=#UserID;
OPEN FileCursor
FETCH NEXT FROM FileCursor INTO #FileID , #UserID
WHILE ##FETCH_STATUS =0
BEGIN
EXEC DeleteFile #FileID,#UserID;
FETCH NEXT FROM FileCursor INTO #FileID , #UserID ;
END
END
END
However , another reply to the question suggested using a Common Table Expression, I googled and I don't think I really understand how can a CTE replace the cursor in this procedure. Any suggestions ?
You can use a recursive CTE to get all children of the ID that should be deleted and then use that in a DELETE statement:
with all_ids as (
select id, ParentID
from FileTree
where id = 4 -- this is the root ID that should be deleted
union all
select c.id, c.ParentID
from FileTree c
join all_ids p on p.id = c.ParentID
)
delete from file_tree
where id in (select id from all_ids);
SQLFiddle example: http://sqlfiddle.com/#!3/ef474f/1
I have a table with certain records, let's call this the Priority table. For each record in this table I need to execute a stored procedure called GetProductsBasedOnPriorityRecord which returns records from a Product table.
All these results need to be returned as one UNION without duplicates.
I'm able to write the following C# pseodo code, but I have no idea on how to do this in SQL:
//DECLARE resultVariable
var products = new List<Product>();
//SELECT * FROM Priority and FOREACH over this
foreach(var prio in Priority) {
//EXEC GetProductsBasedOnPriorityRecord and UNION this on the resultVariable
products.Union(GetProductsBasedOnPriorityRecord(prio));
}
//RETURN resultVariable
return products;
You should not be writing SQL code in loops if you need it to perform even relatively well. But this is what I would do in this case.
-- build some variables
declare #prio table (prio varchar(100) not null)
declare #singlePrio varchar(50)
declare #sqlcmd varchar(50)
-- assuming that the output of the stored procedure is just a table of INTs
declare #output table (outputID int not null)
-- build the list to loop over
insert into #prio (prio)
select * from [Priority]
while exists (select 1 from #prio)
begin
select #singlePrio = (select top 1 prio from #prio)
--
insert into #output exec GetProductsBasedOnPriorityRecord #singlePrio
--
delete from #prio where prio = #singlePrio
end
-- using distinct because you want a 'union' but didnt say 'union all'
select distinct outputID from #output
Well you should be able to do it with a cursor. It's not always the best way. I assume that you are forced to use the existing stored procedure.
Example tables
CREATE TABLE [dbo].[Products](
[ProductID] [int] IDENTITY(1,1) NOT NULL,
[ProductName] [nchar](10) NULL,
[PriorityID] [int] NULL)
CREATE TABLE [dbo].[Priority](
[PriorityID] [int] IDENTITY(1,1) NOT NULL,
[PriorityCode] [nchar](10) NULL)
(with PKs etc).
Sample stored procedure:
ALTER PROCEDURE [dbo].[GetProductsBasedOnPriorityRecord]
-- Add the parameters for the stored procedure here
#PriorityID int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT * FROM Products WHERE PriorityID = #PriorityID
END
Then something like this:
DECLARE #PriorityID INT
DECLARE #table TABLE (ProductID INT, ProductName NCHAR(10), PriorityID INT)
DECLARE cur CURSOR FOR SELECT PriorityID FROM Priority
OPEN cur
FETCH NEXT FROM cur INTO #PriorityID
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #table EXEC GetProductsBasedOnPriorityRecord #PriorityID
FETCH NEXT FROM cur INTO #PriorityID
END
CLOSE cur
DEALLOCATE cur
SELECT DISTINCT * FROM #table
Of course this makes some assumptions about your tables. Also (of course) a synthetic example like this is actually showing a long-winded way of doing something very simple.
Try this one -
DECLARE #SQL NVARCHAR(MAX)
IF OBJECT_ID (N'tempdb.dbo.#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp (outputID INT NOT NULL)
SELECT #SQL = (
SELECT CHAR(13) + '
INSERT INTO #temp(outputID)
EXEC dbo.GetProductsBasedOnPriorityRecord '''
+ prio + ''''
FROM dbo.[Priority]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
PRINT #SQL
EXEC sys.sp_executesql #SQL
SELECT DISTINCT outputID
FROM #temp