2

I expected to be able to include multiple SELECT statements, each separated by a semicolon, in my query, and get a dataset returned with as the same number of datatables as individual SELECT statements.

I am starting to think that the only way that this can be done is to create a stored procedure with multiple refcursor output parameters.

string sql = @"SELECT
                            R.DERVN_RULE_NUM
                            ,P.DERVN_PARAM_INPT_IND
                            ,R.DERVN_PARAM_NM
                            ,R.DERVN_PARAM_VAL_DESC
                            ,P.DERVN_PARAM_SPOT_NUM
                            ,R.DERVN_PARAM_VAL_TXT
                        FROM
                            FDS_BASE.DERVN_RULE R
                                INNER JOIN FDS_BASE.DERVN_PARAM P
                                        ON R.DERVN_TY_CD = P.DERVN_TY_CD
                                            AND R.DERVN_PARAM_NM = P.DERVN_PARAM_NM
                        WHERE
                            R.DERVN_TY_CD = :DERVN_TY_CD
                        ORDER BY
                             R.DERVN_RULE_NUM
                            ,P.DERVN_PARAM_INPT_IND DESC
                            , P.DERVN_PARAM_SPOT_NUM";

        var dataSet = new DataSet();

        using (OracleConnection oracleConnection = new OracleConnection(connectionString))
        {
            oracleConnection.Open();

            var oracleCommand = new OracleCommand(sql, oracleConnection)
            {
                CommandType = CommandType.Text
            };

            oracleCommand.Parameters.Add(":DERVN_TY_CD", derivationType);
            var oracleDataAdapter = new OracleDataAdapter(oracleCommand);
            oracleDataAdapter.Fill(dataSet);
        }
           

I tried to apply what I read here:

https://www.intertech.com/Blog/executing-sql-scripts-with-oracle-odp/

including changing my SQL to enclose it in a BEGIN END BLOCK in this form:

string sql = @"BEGIN 
                 SELECT 1 FROM DUAL;
                 SELECT 2 FROM DUAL;
               END";

and replacing my end of line character

sql = sql.Replace("\r\n", "\n");

but nothing works.

Is this even possible w/o using a stored procedure using ODP or must I make a seperate trip to the server for each query?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Chad
  • 23,658
  • 51
  • 191
  • 321
  • You anoymous pl/sql block doesn't actually have a "return" clause to send anything back. Looking at the example, you need to define variables, select "into" them and then return them. That said, I do not believe this is a widely used construct. Every other program I've seen would place a separate call to the database for each explicit query or command. – pmdba Jun 23 '20 at 22:36
  • 1
    Does this answer your question? [How to use an Oracle Ref Cursor from C# ODP.NET as a ReturnValue Parameter, without using a Stored Function or Procedure?](https://stackoverflow.com/questions/11267746/how-to-use-an-oracle-ref-cursor-from-c-sharp-odp-net-as-a-returnvalue-parameter) - use two output cursors instead of one. – madreflection Jun 23 '20 at 22:58
  • Another [example](https://oradim.blogspot.com/2007/04/odpnet-tip-anonymous-plsql-and.html) with anonymous block . –  Jun 23 '20 at 23:34
  • @pmdba , madreflection 2 I'm an Oracle beginner, but I suspect that the RETURN clause is needed if I am to use a block, which looks like I would probably have to declare a type that matches the return row and then return a table of that type just to get a single result set back. Would the type have to first be created in the block? I don't want to create db objects just to query. Then, I would think that a RETURN statement returns a single variable, how would I return two query results? – Chad Jun 24 '20 at 01:12
  • Why do all your table and column names begin with `DERVN_`? `DERVN_` this, `DERVN_` that - `DERVN_`, `DERVN_`, **`DERVN_`**!!! ***MU-AH-HA-HA-HA-HA-HA-HA!!! I'm going MAD! MMAAADD, DO YOU HEAR ME?!?!? MWA-HA-HA-HA-HA-HA-HA-HA-HA!!!***. Seriously - don't use prefixes like that. Any little thing can trigger flashbacks, y'know..? – Bob Jarvis - Слава Україні Jun 24 '20 at 02:41
  • @chad what do you hope to save with this? Programmer effort? No- both queries must still be written. Bytes? No- both queries must be sent, both resultsets must be retrieved. Time? No- both queries can be sent at the same time over separate channels and production DBs execute hundreds of queries in parallel all the time. Opening an closing a connection? No- connections stay open permanently and live in a pool; opening and closing should be called lease and return. What tangible saving will you achieve? – Caius Jard Jun 24 '20 at 04:23
  • There's `DbDataReader.NextResult()`, but I don't know how that relates to `DbDataAdapter`. – Jeremy Lakeman Jun 24 '20 at 06:15
  • I think you may have identified one of the reasons people don't generally do this. Another would be that handling this in a script would force serial execution of the queries, and might be less scalable. – pmdba Jun 24 '20 at 11:46
  • @BobJarvis-ReinstateMonica No, I really don't know. Flashbacks? Like of your tour in 'Nam? I didn't name these fields. A committee of all knowing corporate architects standardizes everything. – Chad Jul 06 '20 at 13:52

2 Answers2

0

The simplest way to return multiple query results from a single statement is with the CURSOR SQL function. For example:

select
    cursor(select * from all_tables) tables,
    cursor(select * from all_objects) objects
from dual;

(However, I am not a C# programmer, so I don't know if this solution will work for you. Let me know if the code doesn't work - there's probably another solution using anonymous blocks and OUT parameters.)

Jon Heller
  • 34,999
  • 6
  • 74
  • 132
  • I'm thinking the code needs to SELECT INTO declared cursor variables that are declared in a block. I haven''t figured out yet how to do this. – Chad Jul 06 '20 at 13:49
0

must I make a seperate trip to the server for each query?

The way this is asked makes it seem like there's a considerable effort or waste of resources going on somewhere that can be saved or reduced, like making a database query is the equivalent of walking to the shops to get milk, coming back, then walking to the shops again to get bread and coming back

There isn't any appreciable saving to be had; if this was going to the shops, db querying is like being able to clone yourself X times, the X of you all going to the shops, and coming back at different times - some of you found your small things instantly and sprint back with them, some of you found the massive things instantly and stagger back with them, some of you took ages to find your things etc. (These are metaphors for the speed of query execution and the time required to download large vs small result sets).

If you have two queries that take ten seconds each to run, you can set them going in parallel and have your results ready and retrieved to the client in 10+x seconds (x being the time required to drag the data over the network), or you could execute them in series and have it be 20+x

If you think about it, putting two queries in one statement is only the same thing as submitting two statements for execution over different connections. The set of steps the db must take, and the set of steps the client must do to read, are the same. Writing a sproc to handle it is more effort, more complexity to maintain and more places code lives in. Even writing a block to do it is more. None of it saves anything. Even the bytes in the header of the tcp packets, minutiae as they are, are offset by more complex multi line blocks. If one query takes considerably longer than the other you might even be hamstrung into having to wait for them all to finish before you can get the results

Write your "query statement x with y parameters and return resultset Z" as async, start two of them and Task.WhenAll to wait for them to finish; if you can handle it, don't do a WhenAll but instead read and use the results as they finish - that's a saving, if the process can logically proceed before all queries deliver

I get that you're thinking "surely I should just walk to the shops and carry milk and bread back with me - that's more efficient than going twice" but it's a faulty perspective when you consider that the shop is nanoseconds away because you run at the speed of light, you have multiple private unobstructed paths to it and the bigger spend of time is finding the items you want and loading them into sufficient chained-together carts/dragging them all home. With a cloning approach, if the milk is right there, one of you can take it home and spend 10 minutes making the béchamel with it while the other of you is still waiting 10 minutes for the shop to bake the bread that you'll eat directly when you get home - you can still eat in 10 minutes if you maintain the parallelism, and launching separate operations is not only simpler but it keeps you in easy control of that

Caius Jard
  • 72,509
  • 5
  • 49
  • 80
  • In a client app is single threaded and very chatty performance can be radically different depending on the location of the client. (I've seen this with Blue Prism's product). I agree that multi threading probably eliminates the need for what I am looking to do, at the cost of increased client side coding complexity even using async/await. I'm not yet comfortable with writing multi threaded apps, I probably should force myself to learn it. – Chad Jul 06 '20 at 13:48