Get the output of a PRINT sql in string - c#

A small question
I am trying to run a db query through c#.
I am trying to print something on DB which I want to return as a string from C#
string altertable1 = "print 'scfs'";
SqlCommand altertable = new SqlCommand(createtablecommand, connection);
string x = altertable.(function which return the printed query result);
SO what i want is that x value will be scfs;

I don't think there is an easy way to capture the output of PRINT command, since it doesn't return any row readable through a SqlDataReader object.
I would consider a solution that stores the output messages of a query into a temporary table (for example #outputMessages) with just one varchar column. When the execution of your query is completed, I would capture its output messages stored into #outputMessages table with just a SELECT * FROM #outputMessages.
When the output messages capturing process is completed, just drop the temporary table.
Please also read the question linked by #Corak.

So I did it in a different manner ,
I declared a variable , and get my print statement data in that variable and rather than printing the variable , i select that variable in the last part.
Now for C# , i use altertable.ExecuteScalar which return the first row as string, so for me it was what I needed.
string altertable1 = "print 'scfs'";
SqlCommand altertable = new SqlCommand(createtablecommand, connection);
string x = altertable.(function which return the printed query result);

Related

Retreiving a subset of data from an Oracle database

I have an Oracle 19c database that I am trying to pull data from using a package procedure. It is working, but I am new to Oracle, previously very experienced in Microsoft SQL Server. The C# code I have below works and calls my stored procedure successfully. However, the stored procedure returns over one million rows. I do not want to have a DataSet filled with over a million rows because obviously this is very slow. I would like to return is a subset, like offset X rows and take N rows. Basically I want to do something like this:
SELECT * FROM STORED_PROCEDURE OFFSET 50 ROWS FETCH NEXT 50 ROWS ONLY
But I want to do it using my package procedure. Here is my C# code:
public async Task<List<DbModels.DocumentWipList>> GetWipDocumentsAsync(string sort = "limited_dodiss ASC")
{
using (var connection = new OracleConnection(_configuration.GetConnectionString("OracleDev")))
{
using (var command = connection.CreateCommand())
{
connection.Open();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "PKG_GET_COMPONENT_DETAIL.pr_get_wip_comp_list_sorted";
command.Parameters.Add("arg_sort", OracleDbType.Varchar2).Value = sort;
command.Parameters.Add("io_cursor", OracleDbType.RefCursor).Direction = ParameterDirection.Output;
using (var da = new OracleDataAdapter())
{
da.SelectCommand = command;
var dt = new DataTable();
await Task.Run(() => da.Fill(dt));
return MapDocumentWipList(dt);
}
}
}
}
It should be noted that I cannot modify the package procedure. I am hoping there is an easy way to do this, perhaps by somehow wrapping the package procedure as a subquery for a SELECT query.
In Oracle, a cursor is effectively a pointer to a memory address on the database server where the database stores the query the cursor executes and the current execution state of the cursor (a cursor never stores the result set waiting to be read, that is effectively generated on the fly as each row is read). You can read from a cursor once but you cannot change the cursor or rewind it.
I would like to return is a subset, like offset X rows and take N rows.
Don't use await Task.Run(() => da.Fill(dt));. Instead, read and ignore X rows for the cursor and then read and store N rows.
However, it would be better to change the procedure to allow pagination.
What about filtering? One of the columns that comes back is OWNER_NAME. What if I wanted to pull just the rows WHERE OWNER_NAME LIKE 'R%' or something like that?
It is impossible to modify a cursor, if you have to read it then you will need to read ALL the rows for the cursor and discard the rows that do not match your condition. So again, don't use await Task.Run(() => da.Fill(dt));, which would load all the rows into memory, instead read the rows one-by-one and only keep the ones you want in memory and forget the rest.
You can write a second procedure in PL/SQL to wrap around the cursor or you can do the processing in a third-party application like C# but you will need to read the cursor. All that changes between doing it in PL/SQL or C# is whether the processing occurs on the database server or on the third-party application server.
For example, if you have the table:
CREATE TABLE table_name (a, b, c) AS
SELECT LEVEL, CHR(64 + LEVEL), DATE '1970-01-01' + LEVEL - 1
FROM DUAL
CONNECT BY LEVEL <= 10;
And an existing procedure (that cannot be changed):
CREATE PROCEDURE get_cursor (
o_cursor OUT SYS_REFCURSOR
)
IS
BEGIN
OPEN o_cursor FOR
SELECT * FROM table_name;
END;
/
Then you can create the types:
CREATE TYPE table_name_type IS OBJECT(
a NUMBER,
b VARCHAR2(1),
c DATE
);
CREATE TYPE table_name_array IS TABLE OF table_name_type;
Which allows you to create a pipelined function:
CREATE FUNCTION wrap_cursor_fn (
i_cursor IN SYS_REFCURSOR
) RETURN table_name_array PIPELINED
IS
v_a table_name.a%TYPE;
v_b table_name.b%TYPE;
v_c table_name.c%TYPE;
BEGIN
LOOP
FETCH i_cursor INTO v_a, v_b, v_c;
EXIT WHEN i_cursor%NOTFOUND;
PIPE ROW (table_name_type(v_a, v_b, v_c));
END LOOP;
CLOSE i_cursor;
EXCEPTION
WHEN NO_DATA_NEEDED THEN
CLOSE i_cursor;
WHEN OTHERS THEN
CLOSE i_cursor;
RAISE;
END;
/
Which then allows you to use the returned pipelined collection in an SQL statement and read one cursor into another cursor and apply filters to it:
CREATE PROCEDURE wrap_cursor_proc (
i_cursor IN SYS_REFCURSOR,
o_cursor OUT SYS_REFCURSOR
)
IS
BEGIN
OPEN o_cursor FOR
SELECT *
FROM TABLE(wrap_cursor_fn(i_cursor))
WHERE MOD(a, 3) = 0;
END;
/
Which you can then read:
DECLARE
v_cur1 SYS_REFCURSOR;
v_cur2 SYS_REFCURSOR;
v_a table_name.a%TYPE;
v_b table_name.b%TYPE;
v_c table_name.c%TYPE;
BEGIN
get_cursor(v_cur1);
wrap_cursor_proc(v_cur1, v_cur2);
LOOP
FETCH v_cur2 INTO v_a, v_b, v_c;
EXIT WHEN v_cur2%NOTFOUND;
DBMS_OUTPUT.PUT_LINE( v_a || ', ' || v_b || ', ' || v_c );
END LOOP;
CLOSE v_cur2;
END;
/
and outputs:
3, C, 03-JAN-70
6, F, 06-JAN-70
9, I, 09-JAN-70
fiddle
It is a lot of work to read a cursor within the database and get it back into a format that can be used in an SQL query so that you can wrap it in another cursor just to apply a filter. It would be much simpler to just duplicate the original procedure and add the necessary filter condition to it.

Query return on C#/mysql

I have in my class "fattura" this
public string sommaFattura(String costo)
{
MySqlCommand command = new MySqlCommand();
String sumQuery = "SELECT SUM(`prezzo`) FROM `fatturetemp`";
command.CommandText = sumQuery;
command.Connection = conn.getConnection();
command.Parameters.Add("#prezzo", MySqlDbType.Int32).Value = costo;
conn.openConnection();
//Need the command for take the result
conn.closeConnection();
}
What is the command for take the result of Sum query?
I want use this command, insert in a variable. Can you correct me?
I think the command you want is
command.ExecuteScalar();
However, note, Execute Scalar is intended that the result will return a single row, single column which your query does without regard to your parameter. However your query does not specifically make sense either..
Now your query itself. You have an explicit prezzo which would imply an existing column name and not that of a parameter. It would have to a numeric field for obvious reasons to sum it.
Now you also have an expected string coming in via "costo" parameter to the function call. If your intent is to have the incoming string be representative of a known column of the table, this is not going to work for you. You would have to build the SQL command with that explicit column name, or build doing dynamic-sql, but that would really be too much.
The concern with building a SQL statement with string parameters is you would be open to SQL-injection, especially if web-based. If your incoming string is coming from a controlled source, such as you are presenting a list of columns to a user and they can only pick one such column. Or, you have buttons on a screen asking for a sum of a certain thing, and YOU control that column name you would be in a better condition, but still be cautious passing parameter to build out SQL.
Say for example your table has numeric fields of qtySold and dollarSales. You want either the sum of either of these columns and are passing one of these respective strings into the function such as
var qtySoldAnswer = sommaFattura( "qtySold" );
or
var dollarSalesAnswer = sommaFattura( "dollarSales" );
Then your function would be CLOSER to...
public string sommaFattura(String costo)
{
MySqlCommand command = new MySqlCommand();
command.CommandText = "SELECT SUM(" + costo + ") FROM fatturetemp";
command.Connection = conn.getConnection();
conn.openConnection();
var answer = command.ExecuteScalar();
conn.closeConnection();
// I would put a breakpoint in here to see the results.
// you can then format the answer such as decimal point, etc.
return answer.ToString();
}
AGAIN, ONLY if YOU have control of the column name you are trying to send in. Someone could do sql-injection and do a lot of damage. I would only do with heavily controlled consideration and explicitly validating the columns you WOULD allow, and if so, set your variable at that point. Don't allow for any more or less than the column name. No special characters, quotes, comments, sql-terminator and new statement...
But hopefully this clarifies what I THINK you are trying to accomplish.

Reading results of SP with OdbcDataReader gives Error 22018 - Error in assignment

Ok I don't get this. I haven't used ODBC classes before but figuered it's nothing special for basic use. And it does work except in this case.
I need to execute stored procedure without parameters via ODBC connection and get the results, parse the rows into objects and insert them in my local DB. And it worked with test data but now fails with live data, while customer is able to execute the same PS via some other tool... The real trouble is that I have to run it on live server, so I can't debug, instead I created small project which writes output into TextBox. Anyway, here's the code:
var ODBCConnection = new OdbcConnection();
ODBCConnection.ConnectionString = "something...";
//using command "exec schema.spName" or "exec schema.spName()" or "{ call schema.spName()}" runs the procedure
//putting only name "schema.spName" gives ERROR [42000]
var cmd = new OdbcCommand("exec schema.spName())", ODBCConnection);
cmd.CommandType = CommandType.StoredProcedure;
DbReader = cmd.ExecuteReader();
int fCount = DbReader.FieldCount;
infoBox1.Text += System.Environment.NewLine + "Results:";
for (int i = 0; i < fCount; i++)
{
String fName = DbReader.GetName(i);
infoBox1.Text += fName + "|";
}
This list all the column names in result and there are 20 columns.
while (DbReader.Read())
{
var row = new RowClass();
for (int i = 0; i < fCount; i++)
{
object val = DbReader.GetValue(i);
//check which column this is and parse it to set properties of RowClass
//Expected values are string, int and decimal
}
}
This works for the first 10 rows but breaks when it tries to read for following columns with error:
ERROR [22018] [Cache ODBC][State : 22005][Native Code 22005]
[path to .exe]
Error in assignment
No StackTrace no InnerException.
I tried skipping 11th column because it started there, but breaks for every column after the first 10.
I am clueless... if it read there are 20 fields then wtf... null values are not problem because it returns DBNull, it works on other places (not executing SP but doing select queries).
Client executed sp connectin from same net environment and send me picshot, and csv of data. Nothing strane in data itself.
Anyone had this before? Should I use something else for instead of OdbcDataReader?
Thank you.
OK, I managed to figure it out thanks to customer admin who was executing the same procedure by some browser SQL tool. There I saw that 11th column was of type Date. ODBC returned type INT for that column. So I had to call another admin who set up ODBC and he changed column type to varchar so now it finally works.
I guess I can now assume how DataReader works if one wrong column type made all subsequent columns impossible to read as well.
I don't feel like accepting my own answer, so if someone would be kind enough to give me few links about how ODBC works and how DataReader works, not just how to use it, and maybe can quote some text that explains this behavior for this kind of mistake...

OleDb Parameter Into OleDbCommand Not Passing into Query

I'm attempting to pass a string of numbers into a query that will run against our AS400 connection however it doesn't seem that my value is actually making it to the query. I am returning a result when I debug to the point of the parameter so I know I'm getting my number.
var command = new OleDbCommand("SELECT UMACT, UMCUS, UMNAM, UMAD1, UMAD2, UMAD3, UMZIP, UMOPH, UMSLC FROM CFFILES.UMST WHERE trim(UMEMT) = '?' ", connection);
//Test meter number: 59115796
connection.Open();
OleDbParameterCollection paramCollection = command.Parameters;
paramCollection.Add(meternumber.ToString(), OleDbType.LongVarChar);
When I pass a number directly into the query it returns a perfect result however when I try to pass a number from the parameter it returns null.
Here is the variable itself that I know is being passed to my parameter that I created above because I get a result when I debug to that line.
public IEnumerable<CustomerInfoModel> GetCustomerInfo()
{
int meternumber = 59115796;
getAS400Data(meternumber);
return customerList;
}
For my AS400 query to work I have to have the ticks around the ? in the query so I'm not sure if that is perhaps causing the issue.
You have the call to Add method wrong. The first parameter of Add is the Name of the parameter, the second is the DataType and finally you should set the value of the parameter in this way
OleDbParameterCollection paramCollection = command.Parameters;
paramCollection.Add("whatever", OleDbType.LongVarChar).Value = meternumber.ToString();
See OleDbParameterCollection.Add
And no, you shouldn't add ticks around the question mark placeholder. If you do you transform your placeholder in a literal string and obviously you don't have any row with a question mark as value.
A final note, in OleDb parameters doesn't have a name, but when you add them to your collection you should provide one so you are free to choose whatever you like for the name. Just remember to keep them in the same order in which the placeholders (?) appears in your query string

Varying number of records

I am executing a SQL Server stored procedure from my C# code which essentially pulls some data from a database based on the supplied condition.
DataSet GetAllxxxxxByDate(string entityValue,string companyValue)
{
using (var sqlConn = new SqlConnection(GetConnectionString()))
{
using (var cmd = new SqlCommand())
{
var data = new DataSet();
cmd.CommandText = “myStoredprodecure”;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = sqlConn;
var eVal = string.IsNullOrWhiteSpace(entityValue) ? string.Empty : entityValue;
cmd.Parameters.AddWithValue("#entity_value", eVal);
var company = string.IsNullOrWhiteSpace(companyValue) ? string.Empty : companyValue;
cmd.Parameters.AddWithValue("#company", company);
var sqlDataAdaptor = new SqlDataAdapter(cmd);
sqlDataAdaptor.Fill(data);
return data;
}
}
}
Here entityValue, companyValue are comma separated strings, formed dynamically within C# code and pass it to stored procedure.
Eg:
’first’,’second’,’third’
And the stored procedure uses these values to fill the NOT IN condition defined within it.
The issue is that, I am getting inconsistent number of records when I execute the code.
Following is a quick screenshot where first WHERE clause return 3 records and second WHERE clause return 1 record. The input values for the first WHERE clause is been filled from c# code and the second is been filled manually to test.
The only difference, which I can spot is number of quotes.
Question: can someone help me to zero in the issue or the difference in these give WHERE clause ?
Well, you don't show what entity_value is in your results, but the difference between the two is you're adding single quotes around the literal values:
N'''FSEC''' in SQL is the literal valiue 'FSEC'
'FSEC' in SQL is just FSEC (without the quotes).
My guess is that records 2004981 and 2004982 have a value of FSEC (without the quotes) for entity_value.
If you're adding parameter values from C# code, don't add quotes around them like you would if you were building a string. SQL will treat the values as strings without needing string qualifiers.
EDIT
Okay, I just read this statement:
Here entityValue, companyValue are comma separated string
You can't just pass in a comma-delimited string to an IN clause. To search for multiple values there are a few options:
Add commas to each end and use LIKE:
Where (',' + #entity_value +',' LIKE '%,' + entity_value + ',%')
Parse the string into a temporary table, than use that table in your IN clause
Pass the values as a table-valued parameter and use that in your IN clause.
Build the SQL statement as a string and execute it with EXEC

Categories