Showing posts with label excute. Show all posts
Showing posts with label excute. Show all posts

Sunday, February 26, 2012

Execute Process Task using StandardInputVariable

Greetings!

I am using a Excute Process Task calling a selfwritten EXE and I'm trying to pass a command line argument to it. The code for the EXE is quit simple:

Public Sub Main()
Try
Dim info As String = My.Application.CommandLineArgs.Item(0)
MessageBox.Show("Info:" & info)
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub

According to the help files there are two ways to pass an argument, using the property Arguments, which I guess is for hard-coded stuff, and the StandardInputVariable for passing dynamic info.

When I use the Arguments property everything works fine. I get the info messagebox with correct data. But when I leave that blank and instead use the StandardInputVariable property setting it to my package variable User::Info then I crash into an exception meaning that no arguments existed (array is out of bonds).

The variable User::Info was at first filled from a previous SSIS task and using the OnPreExecute breakpoint I verified that it contained a stringvalue. I then hardcoded a string value into the variable, but nothing helps. The task refuses to start my EXE with the data in the StandardInputVariable as an argument.

Why is this not working?

When you use the StandardInputVariable property setting, are you declaring the StandardInputVariable property in the code for the EXE?|||

I am not sure what you mean by this. The StandardInputVariable is just a property in the SSIS task Execute Process. It's supposed to just pass the content of the selected variable as a start-up argument to the selected EXE. Is there some kind of a global variable inside my VS2005 VB project that automatically gets some info that is not passed as an argument? Or can I declare something like that?

All my EXE file should need to do is look at the arguments (the first only in this case) passed to it like this:

Dim arg as string = My.Application.CommandLineArgs.Item(0)

If i use the Argument propert it works fine. But that is for hard coded start-up values like standard /H for help or /Q for quiet. I need to pass a dynamic value and that's what StandardInputVariable is for. I have verified that my SSIS variable contains the correct data. Yet no argument is delivered to the EXE file.

|||

Use an expression on the Arguments property for passing both static and dynamic command line arguments to an execute process task.

By using an expression, you can incorporate any combination. For example, the expression on Arguments could be: "/Q" + @.User::FilePath

The StandardInputVariable is a task property pointing to a variable who's contents will be streamed in on the process' standard input file handle. If you log the Execute Process specific event entitled "ExecuteProcessVariableRouting", and make use of the StandardInputVariable task property, note that that the log message says, "Routing stdin from variable "User:<your variable here>".

|||

It turns out that you need to include a call to Console.Readline, as follows:

Dim info As String = Console.Readline

And, of course, make sure that the StandardInputVariable property is set to the package variable containing the value you want to pass. I also left the Auguments property setting blank when I tested this procedure.

Carla

|||

Thank you very much. This solved the problem.

I never thought of using Console since my application is a Windows Forms executable, but it worked like a charm.

|||

Please!

Is it possible to link two o more system variables (StartTime + UserName) in a user variable?

I tried Name=User::MyVar and Value=System::StartTime + "Test" but when I try to read by using your

Dim Info As String = Console.ReadLine

it give me the string "System::StartTime + "Test""

Thanks in advance.

Alex.|||

Not sure if I completely understand your question, but it sounds like you need to use an expression for your variable. You can do this by setting the EvaluateAsExpression property of the variable to TRUE, and then enter your expression (i.e., User::MyVar + User::MyVar2) into the Expression property.

You may need to cast the variables to concatenate them. The expression builder will help you validate that the expression is correct.

Execute Process Task using StandardInputVariable

Greetings!

I am using a Excute Process Task calling a selfwritten EXE and I'm trying to pass a command line argument to it. The code for the EXE is quit simple:

Public Sub Main()
Try
Dim info As String = My.Application.CommandLineArgs.Item(0)
MessageBox.Show("Info:" & info)
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub

According to the help files there are two ways to pass an argument, using the property Arguments, which I guess is for hard-coded stuff, and the StandardInputVariable for passing dynamic info.

When I use the Arguments property everything works fine. I get the info messagebox with correct data. But when I leave that blank and instead use the StandardInputVariable property setting it to my package variable User::Info then I crash into an exception meaning that no arguments existed (array is out of bonds).

The variable User::Info was at first filled from a previous SSIS task and using the OnPreExecute breakpoint I verified that it contained a stringvalue. I then hardcoded a string value into the variable, but nothing helps. The task refuses to start my EXE with the data in the StandardInputVariable as an argument.

Why is this not working?

When you use the StandardInputVariable property setting, are you declaring the StandardInputVariable property in the code for the EXE?|||

I am not sure what you mean by this. The StandardInputVariable is just a property in the SSIS task Execute Process. It's supposed to just pass the content of the selected variable as a start-up argument to the selected EXE. Is there some kind of a global variable inside my VS2005 VB project that automatically gets some info that is not passed as an argument? Or can I declare something like that?

All my EXE file should need to do is look at the arguments (the first only in this case) passed to it like this:

Dim arg as string = My.Application.CommandLineArgs.Item(0)

If i use the Argument propert it works fine. But that is for hard coded start-up values like standard /H for help or /Q for quiet. I need to pass a dynamic value and that's what StandardInputVariable is for. I have verified that my SSIS variable contains the correct data. Yet no argument is delivered to the EXE file.

|||

Use an expression on the Arguments property for passing both static and dynamic command line arguments to an execute process task.

By using an expression, you can incorporate any combination. For example, the expression on Arguments could be: "/Q" + @.User::FilePath

The StandardInputVariable is a task property pointing to a variable who's contents will be streamed in on the process' standard input file handle. If you log the Execute Process specific event entitled "ExecuteProcessVariableRouting", and make use of the StandardInputVariable task property, note that that the log message says, "Routing stdin from variable "User:<your variable here>".

|||

It turns out that you need to include a call to Console.Readline, as follows:

Dim info As String = Console.Readline

And, of course, make sure that the StandardInputVariable property is set to the package variable containing the value you want to pass. I also left the Auguments property setting blank when I tested this procedure.

Carla

|||

Thank you very much. This solved the problem.

I never thought of using Console since my application is a Windows Forms executable, but it worked like a charm.

|||

Please!

Is it possible to link two o more system variables (StartTime + UserName) in a user variable?

I tried Name=User::MyVar and Value=System::StartTime + "Test" but when I try to read by using your

Dim Info As String = Console.ReadLine

it give me the string "System::StartTime + "Test""

Thanks in advance.

Alex.|||

Not sure if I completely understand your question, but it sounds like you need to use an expression for your variable. You can do this by setting the EvaluateAsExpression property of the variable to TRUE, and then enter your expression (i.e., User::MyVar + User::MyVar2) into the Expression property.

You may need to cast the variables to concatenate them. The expression builder will help you validate that the expression is correct.

Sunday, February 19, 2012

Execute multiple statements at once...

I am not even sure if this is possible but I want to be able to excute several statements (or SPs) at once from inside a SPs (or any other method).

What I am doing is a I am taking data from a single column, multiple rows and making it into one row (i.e. data1 + data2 + data3....) But I am doing this to a total of 2.1 million individual rows and the result will be about 204k rows.... what I have written is basically a nested loop and it works fine but very slow... slow as in it has been running for 24 hours now and it is about 60% done... I need this to finish in under 36 hours preferably...

If I could handle more than one set of data at once (there will be no duplicates) I could speed up the process by how ever many I feel like working with...

So is there a way to execute a statement (sp or function) and go to the next statement without waiting for the first to finish?

Thanks Big Time... I hope this is possible.

OH.... SQL 2000 SP4 on Windows 2003.

You could open multiple connections from the client, but something tells me that there may be a better way to process your data.

Could you post the relevent schema and code?

|||

AFAIK within SQL server 2000 you do not have such a chance. On a single CPU, I doubt the case would be different under 2005 (but not an MSSQL expert).

However "24 hours" and even "hours" for 2.1 million rows processing to create a 204K data sounded to be very slow to me. Maybe if you select the data outside of SQL server with an isolation like uncommitted, prepare outside and do a single bulk load, it'd be much faster.

How do you do this? All in a single T-SQL batch?

|||

It is all within a single tsql SP.... It is extremely slow and I can't seem to get it to use more processor or memory...

I am thinking using a DTS package and working with multiple temp tables. Then combining all the temp tables in the end... it should be quicker that way and I might be able to push my server (which is enterprise with 12GB of ram and 2 processors)...

current schema is :

Final Table = ID_NUM (Key, char(10)), data (varchar(6000))

Working table is ID_NUM, date varchar(100), importance (smallint) - key is ID_NUM and DATA

Working temp table data varchar(100), importance smallint -- no key, always less than 200 rows.
Plus one variable @.data that is a varchar(6000)

Loop 1

Grab an ID_NUM

get list of data for ID_NUM into working table

Loop 2

Loop through till all rows have been added together or it goes over 6000 characters

@.data = Data + data + data

end loop 2

insert ID_NUM, @.DATA into final table

end loop 1

That is what I am going... no cursor, just while loops...

|||

William:

Where you say, "... date varchar (100), ... " for your "Working table" do you really mean "... data varchar (100) ... "?

Dave

|||

Yes that is what I mean...

sorry... working on a million things a once and don't always proof read..

|||

It sounds like you're just trying to concatenate all of your rows in the child table into a single row in a third table. If so, the following scenario may help.

http://databases.aspfaq.com/general/how-do-i-concatenate-strings-from-a-column-into-a-single-row.html

You'll likely not get much parallelism in looping code that you desribed so if you can find a set-based method of processing then you'll be much better off.

|||

That actually looks good... the problem will be hitting the varchar limit.

I will have to see if I hit it or not...

What fun... lol...

Thanks for all the help.

|||

My apologies - I forgot you're on SQL Server 2000. The XML method of processing won't work in this situation. You still want a process where you can build the comma-separated list in a SET operation rather than one row at a time.

There's a method whereby you can assign a value to a variable in one row and use it in subsequent rows of an UPDATE statement. AFAIK, it's undocumented but it works in SQL 2005 as well so you're safe for a little while.

The pseudo-code would look something like this:

-- Place all of the data into a work table

CREATE TABLE #WorkTable (ID_NUM char(10), data varchar(6000), importance smallint, IdentCol INT IDENTITY, PRIMARY KEY(ID_NUM, IdentCol)

INSERT INTO #WorkTable (ID_NUM, data, importance)

SELECT ID_Num, data, Importance

FROM SourceTable

ORDER BY ID_NUM, Importance

-- Update work table. Each row has the data of itself + the row preceding it when there is a matching ID_NUM

DECLARE @.ID_NUM CHAR(10)

DECLARE @.data VARCHAR(6000)

UPDATE #WorkTable

SET @.data = data = CASE WHEN ID_NUM <> @.ID_NUM THEN data

ELSE @.data + data

END

,@.ID_NUM = ID_NUM

INSERT INTO FinalTable(ID_NUM, Data)

SELECT ID_NUM, MAX(DATA)

FROM #WorkTable

GROUP BY ID_NUM

In case the pseudo-code doesn't work, here's a working sample for Adventureworks database:

CREATE TABLE #JobTitles (job_id INT, job_desc VARCHAR(100), employees VARCHAR(1000), EmployeeName VARCHAR(100), PRIMARY KEY (job_id,

EmployeeName))

DECLARE @.job_id INT, @.EmployeeNames VARCHAR(1000)

INSERT INTO #JobTitles(job_id, job_desc, EmployeeName) SELECT J.job_id, J.job_desc, E.lname +', ' + E.fname AS EmployeeName FROM jobs J INNER JOIN employee E ON J.job_id = E.job_id

SET @.job_id = -1 -- Initialize this to a number that does not exist

-- in the list of values.

UPDATE #JobTitles

SET @.EmployeeNames = employees = CASE

WHEN @.job_id <> job_id THEN EmployeeName

ELSE @.EmployeeNames + '; ' + EmployeeName

END

, @.job_id = job_id

-- Grab the largest row of each grouping.

SELECT job_id, job_desc, MAX(employees) AS EmployeeList FROM #JobTitles GROUP BY job_id, job_desc ORDER BY job_id

DROP TABLE #JobTitles

|||

William:

Here is yet another example. I mocked this up with 2.1 million rows. I was able to get this to run in about 3 minutes and 20 seconds. This problem is definitely easier to solve with 2005 than 2000 and there has to be a better way of doing this, but this example might also help you see some direction; I hope it is helpful.

Dave

I used these two tables and faked the data:

create table dbo.final
( [key] char (10) not null,
[data] varchar (6000) not null
)

create table workingTable
( ID_Num varchar (10) not null,
[data] varchar (100) not null,
importance smallint not null,

constraint pk_mockInput primary key (ID_Num, [data])
)


insert into workingTable
select convert (varchar (5), a.iter),
'{ ' + convert (varchar (5), b.iter) + ' }'
+ replicate ('-', 1 + b.iter % 29 ) + '-->',
b.iter
from small_iterator a (nolock)
inner join small_iterator b (nolock)
on a.iter <= 21300
and b.iter <= 40 + a.iter % 119
and b.iter <= 200
order by a.iter,
b.iter

update statistics workingTable
exec sp_recompile workingTable

I would prefer not to use the function because there is a fair amount of overhead associated with functions; nonetheless, I implemented the method using the following function:

alter function dbo.assembleData
( @.arg_key varchar (10),
@.arg_minData varchar (100),
@.arg_maxData varchar (100)
)
returns varchar (6000)
as

begin

declare @.retValue varchar (6000)
set @.retValue = ''

select @.retValue = @.retValue + [data]
from workingTable
where [id_num] = @.arg_key
and [data] >= @.arg_minData
and [data] <= @.arg_maxData

return ( @.retValue )

end

I then tested out the overall procedure with this query:

--truncate table dbo.final

-- The whole thing takes about 3 minutes and 20 seconds
create table #sequencer
( [key] varchar (10) not null,
[data] varchar (100) not null,
importance smallint not null,
seq integer not null,
siz integer not null,

constraint pk_#sequencer primary key ([key], [data])
)

insert into #sequencer
select a.id_num,
a.[data],
a.importance,
count(*) as seq,
sum (datalength (b.[data])) as siz
from workingTable a
inner join workingTable b
on a.id_num = b.id_num
and b.[data] <= a.[data]
group by a.id_num, a.[data], a.importance
order by a.id_num, a.[data]

insert into dbo.final
select [key],
dbo.assembleData ( [key], min_data, max_data )
from ( select [key],
min ([data]) as min_data,
max ([data]) as max_data,
min (seq) as min_seq,
max (seq) as max_seq,
case when siz = 0 then 0
else siz - 1
end / 6000 as segment
from #sequencer
group by [key],
case when siz = 0 then 0
else siz - 1
end / 6000
) x

drop table #sequencer

select top 20 *
from dbo.final

|||

I will give that method a try...

I have been trying all kinds or things including DTS with multiple connections

all have been unsuccessful in speedy results... my last attempt took about 10 hours

|||

THAT WORKED GREAT!!!!!

My job now completes in 1 min 43 sec.... HUGE improvement...

Thanks.

|||

Great you solved it.

Would you tell me which method you used (I know that not the one I said:). I'm asking because I'm not SQL server oriented and for the last few days seriously diving into SQL server. Before I was using it "superficially". Though I was impressed with T-SQL in 2000, 2005 made me an addict:)

|||I used the solution posted by Jared Ko