Convert select into stored procedure best approach - sql

I use this SQL to get count for every group of type.
select
mytype, count(mytype)
from types1
group by 1
The result is 5 records with count for each type. I need to convert this to a stored procedure; should I write the above SQL using For...Select or should I return single value using Select...Where...Into 5 times for each type?
I will use the return counts to update a master table and types may increase in the future.

That depends on what you want out of the procedure:
If you want the same output as your select with five rows, use a FOR SELECT. You will get one row for each type and an associated count. This is probably the "standard" approach.
If however you want five output variables, one for each count of each type, you can use five queries of the form SELECT COUNT(1) FROM types1 WHERE mytype = 'type1' INTO :type1. Realize though that this will be five queries and you may be better off doing a single FOR SELECT query and looping through the returned rows in the procedure. Also note that if you at some point add a sixth type you will have to change this procedure to add the additional type.
If you want to query a single type, you can also do something like the following, which will return a single row with a single count for the type in the input parameter:
CREATE PROCEDURE GetTypeCount(
TypeName VARCHAR(256)
)
RETURNS (
TypeCount INTEGER
)
AS
BEGIN
SELECT COUNT(1)
FROM types1
WHERE mytype = :TypeName
INTO :TypeCount;
SUSPEND
END

Related

PostgreSQL - How to cast dynamically?

I have a column that has the type of the dataset in text.
So I want to do something like this:
SELECT CAST ('100' AS %INTEGER%);
SELECT CAST (100 AS %TEXT%);
SELECT CAST ('100' AS (SELECT type FROM dataset_types WHERE id = 2));
Is that possible with PostgreSQL?
SQL is strongly typed and static. Postgres demands to know the number of columns and their data type a the time of the call. So you need dynamic SQL in one of the procedural language extensions for this. And then you still face the obstacle that functions (necessarily) have a fixed return type. Related:
Dynamically define returning row types based on a passed given table in plpgsql?
Function to return dynamic set of columns for given table
Or you go with a two-step flow. First concatenate the query string (with another SELECT query). Then execute the generated query string. Two round trips to the server.
SELECT '100::' || type FROM dataset_types WHERE id = 2; -- record resulting string
Execute the result. (And make sure you didn't open any vectors for SQL injection!)
About the short cast syntax:
Postgres data type cast

Fetch multiple rows into a variable

I am attempting to fetch multiple unique account ID's (transactions.ID) and store in Variable v_Trxn. The problem is that the query count returns incorrect. Expected result is 300485 rows.
DECLARE
l_Partition transactions.Partitionkey%TYPE;
v_Trxn transactions.ID%TYPE;
l_Count number;
CURSOR v_Trxn_cur IS
SELECT ID
FROM transactions
WHERE Partitionkey > l_Partition;
BEGIN
SELECT dl_common.Get_Partitionkey(aInstitutionId => DL_COMMON.Get_InstitutionId , aDate => add_months(sysdate,-4)) INTO l_Partition FROM Dual;
OPEN v_Trxn_Cur;
LOOP
FETCH v_Trxn_Cur INTO v_Trxn;
EXIT WHEN v_Trxn_Cur%NOTFOUND;
END LOOP;
SELECT COUNT(UNIQUE(ID)) INTO l_Count FROM transactions WHERE ID In v_Trxn;
DBMS_OUTPUT.PUT_LINE(l_Count || ' Number of Unique Sernos');
CLOSE v_Trxn_Cur;
End;
Output: 1 Number of Unique Sernos
If I put the partitionkey directly into the WHERE clause I get the expected number of rows.
SELECT
dl_common.Get_Partitionkey(aInstitutionId => DL_COMMON.Get_InstitutionId,
aDate => add_months(sysdate, -4)) PARTITION
FROM Dual;
Returns: PARTITION 2914365
SELECT
COUNT(UNIQUE(ID)) C_COUNT
FROM transactions C
WHERE C.Partitionkey > 2914365
Returns: C_COUNT 300485
The expected number of rows is fetched.
Please explain what I am doing wrong.
You're repeatedly selecting a single ID into your scalar variable. It can never hold more than a single value at a time. After the first iteration of your loop your variable holds the first ID returned by your cursor query (which is indeterminate because you don't have an order-by clause). After the second iteration your variable holds the second ID returned by the cursor query. It does not, and cannot, hold both values simultaneously.
To hold multiple values you would need to bulk select into a collection type, which needs to be declared at schema level (i.e. an SQL type, not a PL/SQL type) if you really want to use it as part of a later SQL query.
But storing hundreds of thousands of values in a collection is going to consume a significant amount of memory. Without knowing what you're really going to do with the values once you have them it isn't clear if that is just a price you'll have to pay; if you can do your work in batches (of, say, 1000 IDs at a time; if row-by-row processing is appropriate; or if you really want a join as part of a larger query, without holding them as a PL/SQL variable at all.
You need to use VARRAY OR NESTED TABLES FOR THIS PURPOSE.
You can hold multiple values like this-
Type var_dnames IS VARRAY(1000) transactions.ID%TYPE;
v_Trxn var_dnames;
Now "v_Trxn" can hold multiple values from fetch statement.

Return two values from a scalar SQL Function

I have a Scalar SQL function thats returns a decimal value, and this function is used in many stored procedures across my database. Now in some procedures, I need to set a value based on some criteria inside the function. To make it clearer, depending on some variables used in calculating the result of the function, I want to set another variable inside the Stored procedure, and return it to the client.
I don't want to change how the result is returned or the return type of the function. I am thinking of doing it by inserting the new value i want into an sql table and then reading it from the procedure, But is there another or better way to do it?
Thanks
No, you cannot. Functions are severely limited in SQL Server and do not allow any side effects.
What you can do, however, is convert your scalar function into a table function. In it, you can return a table with as many columns as you need, so returning more than one value is not a problem.
You have a couple of options
1) Change it from a function to a stored procedure, and add an output parameter.
2) Change it from a scalar function to a table valued function returning a single row, with the additional value as an additional column.
If you need to preserve the existing function signature then just create a new table valued function that does the work (As per option 2 above), and modify your existing function to select from the new table valued function.
Here is some example code demonstrating this:
-- the original scalar function
CREATE FUNCTION dbo.t1(#param1 INT)
RETURNS INT AS
BEGIN
RETURN #param1 + 1
END
GO
-- a new table valued function, that returns 2 values in a single row
CREATE FUNCTION dbo.t2(#param1 INT)
RETURNS TABLE AS
RETURN (SELECT #param1 + 1 AS [r1], #param1 + 2 AS [r2])
GO
-- the modified original function, now selecting from the new table valued function
CREATE FUNCTION dbo.t3(#param1 INT)
RETURNS INT AS
BEGIN
RETURN (SELECT r1 FROM dbo.t2(#param1))
END
GO
-- example usage
SELECT dbo.t1(1)
SELECT * FROM dbo.t2(1)
SELECT dbo.t3(1)
Table value functions that return a single row are my favorite technique when a single answer from a scalar function just isn't adequate (or slows the query too much). A table can have from zero to many rows. Once I realized a 'table' value function can be limited to returning only one row it became obvious that multiple questions that would require separate scalar functions can be accomplished in a single table value function. It's like a scalar function on steroids. I like to read in all needed data just once into an internal table variable, then manipulate that data assigning it to additional variables as needed, finally assembling the answers for the output 'table' of one record. My database environment is read only, not transaction based. Incredibly useful for large (Mult-TB) historical database like medical information. Frequently used to concatenate fields into an end user friendly 'sentence' to deal with data that can have zero to many values, like patient diagnosis. Outer Apply the table value function on filtered data and it is extremely efficient.

SQL on-demand cache table (possibly using SQL MERGE)

I am working on implementing an on-demand SQL cache table for an application so I have
CacheTable with columns Type, Number, Value
Then I have a function called GetValue( Type, Number )
So I want to have a function that does the following
If (CacheTable contains Type, Number) then return value
Else call GetValue( Type, Number) and put that value into CacheTable and return the Value
Does anyone know the most elegant way to do this?
I was thinking of using a SQL merge.
Not sure how elegant one can get, but we might do it just the way you describe. Query the database
select Value from Tab1 where Type=#type and Number=#num
and if no rows are returned, compute the value, then store it in the database for next time.
However, if the "compute the value" requires the database itself, and we can compute it in the database, then we can do the whole cycle with one database round trip -- more 'elegant' perhaps but faster at least than 3 round trips (lookup, compute, store).
declare #val int
select #val=Value from Tab1 where Type=#type and Number=#num
if ##ROWCOUNT=0 BEGIN
exec compute_val #type,#num,#val OUTPUT
insert into Tab1 values (#type,#num,#val)
END
SELECT #val[Value]--return
The only use for SQL Merge is if you think there may be concurrent users and the number is inserted between above select and insert, giving an error on the insert. I'd just catch the error and skip the insert (as we can assume the value won't be different by definition).

SQL Server Stored Procedure - Use Row Count in Select query

My stored procedure (SQL Server 2005) returns a dataset where one field depends, among other things, on the number of rows returned by the query. I can make a simplified first query that allows me to get ##ROWCOUNT but, in that case, the procedure returns the two sets, which is not what I want.
I tried putting the first query in a WITH statement but haven't found the syntax to extract the row count and put it in a variable that I could use in the second query. An alternative would be to get ##ROWCOUNT from the first query and tell the procedure to return only the result of the second query.
There are probably better ways to do that but my expertise in SQL is quite limited...
Thanks for any help!
Is this what you're looking for? If not, could you please describe your problem in more details (perhaps, with code snippets)
alter procedure ComplicatedStoredProcedure as
begin
declare #lastQueryRowCount int
-- Storing the number of rows returned by the first query into a variable.
select #lastQueryRowCount =
-- First resultset (not seen by caller).
(select count(*) from A where ID > 100)
-- Second resultset. This will be the actual result returned from the SP.
select * from B where SomeDependentField > #lastQueryRowCount
end