OPENXML - SQL Server - sql

My Sp reading XML file data with help of OPENXML in SQL Server.
There is slight problem in this. Here is xml file part
<Name_Address>
<name>JCB SALES PVT</name>
<address>24, SALAROURIA ARENA ADUGODI</address>
<address>HOSUR MAIN ROAD, Honolulu</address>
<country>N</country>
</Name_Address>
and my SQL query is
SELECT
#address = CONVERT(VARCHAR(150), [TEXT])
FROM OPENXML(#idoc,'/Name_Address/address', 0)
WHERE [text] IS NOT NULL
In #address I am getting last address tag value i.e
HOSUR MAIN ROAD, Honolulu
But it should be
24, SALAROURIA ARENA ADUGODI, HOSUR MAIN ROAD, Honolulu
How can I achieve this ?
Help me, guide me to do this.
regards

Your problem isn't specifically to do to with OPENXML.
Your query...
SELECT CONVERT(VARCHAR(150), [TEXT])
FROM OPENXML(#idoc,'/Name_Address/address', 0)
WHERE [text] IS NOT NULL
...returns multiple rows. So when you assign to a variable, it just takes the last of the returned rows.
I've set up an example which uses a cursor to iterate through this. It includes your example document. You can paste this directly in Query Analyser (2000)/Management Studio (2005+) and it will run. all you have to do is add in your commas and spaces (I've just used a space).
DECLARE #hdoc int
DECLARE #doc varchar(1000)
DECLARE #address varchar(150)
DECLARE #thisaddress varchar(150)
set #address = ''
SET #doc ='
<Name_Address>
<name>JCB SALES PVT</name>
<address>24, SALAROURIA ARENA ADUGODI</address>
<address>HOSUR MAIN ROAD, Honolulu</address>
<country>N</country>
</Name_Address>'
EXEC sp_xml_preparedocument #hdoc OUTPUT, #doc
DECLARE addr_cursor CURSOR
FOR SELECT CONVERT(VARCHAR(150), [TEXT])
FROM OPENXML(#hdoc,'/Name_Address/address', 0)
WHERE [text] IS NOT NULL
--select ##FETCH_STATUS
OPEN addr_cursor
FETCH NEXT FROM addr_cursor INTO #thisaddress
WHILE ##FETCH_STATUS = 0
BEGIN
set #address = #address+ #thisaddress + ' '
FETCH NEXT FROM addr_cursor INTO #thisaddress
END
select #address
CLOSE addr_cursor
DEALLOCATE addr_cursor
exec sp_xml_removedocument #hdoc

Related

combine procedure and cursor in sql

newbie here.
I am trying to do self-learning about SQL.
Right now, I am having a problem in combining procedure and cursor.
Hereby the case
The Case
Create procedure named ‘sp4’ that receive StaffName from user’s input
to display StaffName and StaffPosition for every staff which name
contains the word that has been inputted by user.
(create procedure, declare cursor, like)
Hereby the code that I have tried it
My Code
CREATE PROCEDURE sp4 (#name VARCHAR(100))
AS
DECLARE cur2 CURSOR
SCROLL
FOR
SELECT StaffName, StaffPosition
FROM MsStaff
OPEN cur2
DECLARE #pointer AS VARCHAR(100)
FETCH NEXT FROM cur2 INTO #pointer
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT StaffName, StaffPosition
FROM MsStaff
WHERE #pointer = #name
AND StaffName LIKE '%' + #pointer + '%'
FETCH NEXT FROM cur2 INTO #name
END
CLOSE cur2
DEALLOCATE cur2
Your SELECT statement for the cursor indicates SELECT StaffName, StaffPosition but your FETCH statements don't match that (or each other).
The FETCH is going to load the data from the cursor into your memory variables. So you need to supply one variable for each column indicated in the SELECT. For example, if you have
DECLARE #staffName VARCHAR(100)
DECLARE #staffPosition VARCHAR(100)
then you can replace both of your fetches with:
FETCH NEXT FROM cur2 INTO #staffName, #staffPosition

Set a cursor on a table inside a table variable

All,
Trying to set a cursor on a table value inside a table variable, but it does not work. can anyone comment on how I can fix this?
** the code below is called from another stored procedure which provides the value for the tablename variable **
ALTER PROCEDURE [dbo].[usrSetLTDNormDist]
-- Add the parameters for the stored procedure here
#TableName Sysname,
---...
DECLARE #SQLCommand1 NVARCHAR(MAX) = N'
Set #RecCursor1 = Cursor For
Select [Volume], [TRANSDATE] from #TableName'
EXECUTE dbo.sp_executesql #sqlCommand1
-- Open Cursor
Open #RecCursor1
Fetch Next From #RecCursor1
Into #Volume, #TransDate
---...
Add PRINT #SQLCommand1 between the DECLARE and EXECUTE statements to review what is actually being executed. Based on your code snippet, you will see
Set #RecCursor1 = Cursor For
Select [Volume], [TRANSDATE] from #TableName
...that is, the value you set in #TableName is not automagically added to the script. Here's the way I write these things:
DECLARE #SQLCommand1 NVARCHAR(MAX)
SET #SQLCommand1 = replace(N'
Set #RecCursor1 = Cursor For
Select [Volume], [TRANSDATE] from <#TableName>'
,'<#TableName>', #TableName)
PRINT #SQLCommand1
EXECUTE dbo.sp_executesql #sqlCommand1
I use the < > characters to make the replaced values stand out.
This script demonstrates the general technique:
create table T (ID int not null)
go
insert into T(ID) values (99)
go
declare #TableName sysname
declare #ID int
set #TableName = 'T'
declare #SQL nvarchar(max) = N'declare boris cursor for select ID from ' +
QUOTENAME(#TableName)
exec sp_executesql #SQL
open boris
fetch next from boris into #ID
while ##FETCH_STATUS = 0
begin
print #ID
fetch next from boris into #ID
end
close boris
deallocate boris
Producing this output:
(1 row(s) affected)
99
However, I will offer my usual caution - if you're in a situation where you want to operate against multiple tables in the same way, this is usually a sign of a broken data model. Usually there ought to be a single table with additional columns containing data that serves to differentiate the values.

How to keep client databases consistent whenever a coder changes stored proc/table definition/views/triggers etc

I have asked this question before (here), but it never solved my problems.
Here is the scenario:
1. A coder modifies a stored proc/table definition/views etc on his "development server"
2. The modified T-SQL code is tested and passed by another team
3. Now the tested T-SQL code needs to be updated in 20 client databases. (Which is an extremely tough task).
4. Currently, we copy paste the T-SQL code in every db individually. This also results in errors which are resolved only when the client complaints.
We are using SQL Server 2012, and I guess usage of Schema's may resolve this issue. But I don't know how to do it.
Probably you can use the bellow query. Only thing is you must have access to all the databases and all those DBs are in the same server.
-- Provide DB names as CSV
DECLARE #DBNames VARCHAR(MAX) = 'ExpDB,ExpDB_DUP'
-- Provide Your Update Script here
DECLARE #Script VARCHAR(MAX) = 'CREATE TABLE TestTab (Id int IDENTITY(1,1) NOT NULL,
Value nvarchar(50) NULL)'
DECLARE #DBNamesTab TABLE (DBName VARCHAR(128))
INSERT INTO #DBNamesTab
SELECT LTRIM(RTRIM(m.n.value('.[1]','varchar(128)'))) AS DBName
FROM
(
SELECT CAST( '<XMLRoot><RowData>'
+ REPLACE(#DBNames,',','</RowData><RowData>')
+ '</RowData></XMLRoot>' AS XML) AS x
)t
CROSS APPLY x.nodes('/XMLRoot/RowData')m(n)
DECLARE #DBName VARCHAR(128)
DECLARE #ScriptExe VARCHAR(MAX)
DECLARE dbNameCursor CURSOR FOR SELECT DBName FROM #DBNamesTab
OPEN dbNameCursor
FETCH NEXT FROM dbNameCursor INTO #DBName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #ScriptExe = 'USE ' + #DBName + ' ' + #Script
EXEC(#ScriptExe)
FETCH NEXT FROM dbNameCursor INTO #DBName
END
CLOSE dbNameCursor;
DEALLOCATE dbNameCursor;

Table variable not working in dynamic SQL

My trigger is as below ,
Alter TRIGGER [dbo].[LogTable_InsertTrigger] on [dbo].[std_table] AFTER INSERT
as
DECLARE #ColName varchar(50), #QueryText nvarchar(max)
declare #inserted TABLE(
[CountryID] [int] NOT NULL,
[Country] [nvarchar](255) NOT NULL,
[RegionId] [int] NULL
)
insert into #inserted
select * from inserted
DECLARE objCursor CURSOR FAST_FORWARD FOR
select ColName from dbo.getColumnNames('std_table')
OPEN objCursor
FETCH NEXT FROM objCursor INTO #ColName
WHILE ##FETCH_STATUS = 0
BEGIN
set #QueryText= '
insert into dbo.LogTable
(StandardType,Attribute,Action,NEwValue,UserId,ModifiedDate)
select ''Country'','''+#ColName+''',''Insert'','+#ColName+',1,getdate()
from #inserted'
EXEC sp_executesql #QueryText
FETCH NEXT FROM objCursor INTO #ColName
END
CLOSE objCursor
DEALLOCATE objCursor
When I try to insert to table std_table in DA Layer I get the exception Must declare the table variable "#inserted".
I couldn't use the inserted table directly because I am writing a dynamic query, inside which magic tables wont work. So I am trying to dump the data in inserted table to a temp table and to access from that.
I tried with
select *
into #inserted
from inserted
This works, but since my application is accessed by many users over network this will cause data issues. So I can't use this.
Several things wrong here.
That is a table variable, not a user defined table type.
If #temp tables work, why do you think that will cause data issues for multiple users? Each user will get their own version of the #temp table.
If you know there are exactly three columns and you can hard-code the table variable declaration, why do you need to then generate the three sets of inserts dynamically? Aren't the column names CountryID,Country,RegionID?
If you really need to do this dynamically then it seems like you could do this an easier way without an explicit cursor - not that this is necessarily a bad thing or that it will perform any worse than the below, but the cursor is just much more verbose and ugly code:
ALTER TRIGGER [dbo].[LogTable_InsertTrigger]
ON [dbo].[std_table]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
SELECT * INTO #t FROM inserted;
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + CHAR(13) + CHAR(10) + N'INSERT INTO dbo.LogTable
(StandardType,Attribute,Action,NewValue,UserId,ModifiedDate)
SELECT ''Country'','''+ColName+''',''Insert'','+ColName+',1,GETDATE()
FROM #t;'
FROM dbo.GetColumnNames('std_table');
EXEC sp_executesql #sql;
END
GO

How to select xml in 1 stored procedure and pass selected nodes as parameters into another procedure

I have a stored proc that does inserts of “people”. I have an xml document with a bunch of people I want to insert. I want to call my stored proc like below and expect the stored proc to be called for each person in the xml. It is telling me that the stored proc “expects a parameter of #Id” and is failing. #Id is the first param and it appears that my syntax is not allowed. Is there a way to do this without iterating over each person in a cursor? I am using SQL Server 2005.
EXEC Stored_Procedure_That_Inserts_People
SELECT Node.value('Id[1]', 'Int') AS Id
,Node.value('FirstName[1]', 'varchar(50)') AS FirstName
,Node.value('LastName[1]', 'varchar(50)') AS LastName
,Node.value('MI[1]', 'char(1)') AS MI
FROM #PeopleXML.nodes('/ArrayOfPeople/Person') TempXML (Node)
For anybody interested, this is how I implemented my solution based on Tom's answer below:
CREATE PROCEDURE [dbo].[ccIU_PersonBulkImport]
(
#PersonXML as xml
)
AS
BEGIN
SET NOCOUNT ON
DECLARE
#LastName AS varchar(50),
#FirstName AS varchar(50),
#MI AS char(1)
DECLARE People CURSOR FORWARD_ONLY STATIC READ_ONLY FOR
SELECT
Node.value('FirstName[1]', 'varchar(50)') AS FirstName
,Node.value('LastName[1]', 'varchar(50)') AS LastName
,Node.value('MI[1]', 'char(1)') AS MI
FROM #PersonXML.nodes('/ArrayOfPeople/Person') TempXML (Node)
OPEN People;
FETCH NEXT FROM People INTO #FirstName,#LastName,#MI
WHILE (##FETCH_STATUS = 0)
BEGIN
EXEC domIU_People #FirstName,#LastName,#MI -- second stored proc that inserts or updates the person
FETCH NEXT FROM People INTO #FirstName,#LastName,#MI;
END
CLOSE People;
DEALLOCATE People;
END
No.
You cant iterate a stored procedure like that generally they can only take objects as parameters the exception being a table object.
In this example SQL will try and call the SP and then run the select as separate events which is why you are getting the error about the missing parameter.
Your choices are to iterate through the XML and call the SP for each record, refactor the SP to either work using the XML as a separate parameter and break it down in the insert people procedure or refactor the code from the sp into the XML handling logic procedure.
If the stored procedure is a simple insert into the People table then you could create a new stored procedure such as:
CREATE PROCEDURE dbo.Insert_People_From_XML
#people_xml XML
AS
BEGIN
INSERT INTO dbo.People
(
id,
first_name,
last_name,
middle_initial
)
SELECT
Node.value('Id[1]', 'Int'),
Node.value('FirstName[1]', 'varchar(50)'),
Node.value('LastName[1]', 'varchar(50)'),
Node.value('MI[1]', 'char(1)')
FROM
#people_xml.nodes('/ArrayOfPeople/Person') TempXML (Node)
END
If you have business logic (or other logic that you don't want to duplicate) then you may want to reuse your insert stored procedure as it is. In that case, you will have to iterate through the XML nodes. As much as I try to avoid cursors, this would be a time to use one:
DECLARE
#id INT,
#first_name VARCHAR(50),
#last_name VARCHAR(50),
#middle_initial CHAR(1)
DECLARE people_cursor CURSOR FOR
SELECT
Node.value('Id[1]', 'Int'),
Node.value('FirstName[1]', 'varchar(50)'),
Node.value('LastName[1]', 'varchar(50)'),
Node.value('MI[1]', 'char(1)')
FROM
#people_xml.nodes('/ArrayOfPeople/Person') TempXML (Node)
OPEN people_cursor
FETCH NEXT FROM people_cursor INTO #id, #first_name, #last_name, #middle_initial
WHILE (##FETCH_STATUS = 0)
BEGIN
EXEC Your_Proc
#id = #id,
#first_name = #first_name,
#last_name = #last_name,
#middle_initial = #middle_initial
FETCH NEXT FROM people_cursor INTO #id, #first_name, #last_name, #middle_initial
END
CLOSE people_cursor
DEALLOCATE people_cursor
NOTE: This was all written off the top of my head. I don't use XML much, so syntax may need to be corrected, you'll want to add error-handling, etc.