Using binding inside SQL procedures - snowflake-cloud-data-platform

I am having trouble getting the following code to work:
create or replace secure procedure create_wh (wh_name varchar)
returns varchar
language sql
comment = '<string_literal>'
execute as owner
as
begin
create warehouse if not exists :wh_name
warehouse_size = xsmall
auto_suspend = 60
auto_resume = true
initially_suspended = true;
return 'SUCCES';
end;
The idea is that the SP can be called with a name for a warehouse. It errors in unexpected 'if' after the create warehouse statement when trying to run the above code.
I am guessing I am missing something in relation to binding the param to the query, but I can't figure out what.

It is possible to provide warehouse name as parameter by using IDENTIFIER(:wh_name):
create or replace secure procedure create_wh (wh_name varchar)
returns varchar
language sql
comment = '<string_literal>'
execute as owner
as
begin
create warehouse if not exists IDENTIFIER(:wh_name)
warehouse_size = xsmall
auto_suspend = 60
auto_resume = true
initially_suspended = true;
return 'SUCCES';
end;
CALL create_wh('test');
SHOW WAREHOUSES;

Related

prevent duplicate value to submit using stored procedure in sql server

I want to prevent the same #coupon_value in sp to submit and return
any message for validation using csharp but I am not able to how to
make changes in stored procedure.
CREATE PROCEDURE [dbo].[USP_REBATE_CAMPAIGN_RULE_DETAIL_VALIDATE]
#rebate_campaign_seq INT,
#coupon_value Varchar(50)='',
#Type varchar(50)='SERIES'
AS
SET NOCOUNT ON
BEGIN
SELECT rcrd.rebate_campaign_rule_detail_seq AS id,Type_value AS NAME,
'SERIES' AS type,
rcrd.amount_per_range AS Amount
FROM rebate_campaign_rule_detail rcrd (nolock)
INNER JOIN rebate_campaign_rule rcr
ON rcr.rebate_campaign_rule_seq = rcrd.rebate_campaign_rule_seq
INNER JOIN rebate_campaign rc
ON rc.rebate_campaign_seq = rcr.rebate_campaign_seq
WHERE rc.rebate_campaign_seq = #rebate_campaign_seq
AND rcrd.active_flag = 'Y' AND rcrd.type = #Type
AND rcrd.type_value=#coupon_value
End
The only way to prevent duplicate in database data is to add a UNIQUE CONSTRAINT. Everything else will fail, especially any solution coded with a procedural program, because of concurrency (imagine for a moment that two users launch the same procedure with the same values at the same time...).
To have a NULLbale UNIQUE constraint, you can add a UNIQUE filtered INDEX like this one :
CREATE INDEX X_UNIQUE_COUPON_RCRD
ON rebate_campaign_rule_detail (type_value)
WHERE type_value IS NOT NULL;

Is it possible to ignore errors on DROP VIEW IF EXISTS xxx (Object found is of type TABLE)?

If have a
DROP VIEW IF EXISTS mydatabase.myschema.myname;
CREATE OR REPLACE TABLE mydatabase.myschema.myname AS ...
that fails with error code 2203 SQL compilation error: Object found is of type 'TABLE', not specified type 'VIEW'..
My intention was to create a script to "convert" a set of existing views into tables (updated periodically via tasks). I wanted the script to be repeteable, so I thought I could DROP VIEW IF EXISTS xxx to drop the view if it exists but it seems that this will fail if there is already a table of the same name. So first time the script runs ok, it drops the view and creates the table but if I run the script again it will fail because now there is table with that same name.
So is there any way to ignore the error in the DROP VIEW IF EXISTS xxx or just to run the command if there is a VIEW with that name?
You have a number of options.
You can have your script read from the INFORMATION_SCHEMA to get a list of views and to delete. This SQL gets a list of all views except in the INFORMATION_SCHEMA.
select * from INFORMATION_SCHEMA.VIEWS where TABLE_SCHEMA <> 'INFORMATION_SCHEMA';
If you just want to drop the view names and avoid running into errors, here's a stored procedure you can call to try dropping a view without generating an error:
create or replace procedure DropView(viewName string)
returns string
language JavaScript
execute as OWNER
as
$$
var sql_command =
'drop view ' + VIEWNAME;
try {
var stmt = snowflake.createStatement( {sqlText: sql_command} );
var resultSet = stmt.execute();
while (resultSet.next()) {
outString = resultSet.getColumnValue('status');
}
}
catch (err) {
outString = err; // Return a success/error indicator.
}
return outString;
$$;
If you want to loop through every database and schema in the entire account, I wrote a stored procedure to do that. It's designed for dependency checking on all views, but could be modified to delete them too.
https://snowflake.pavlik.us/index.php/2019/10/14/object-dependency-checking-in-snowflake
My suggestion would be to create a stored procedure that loops through all of your views and creates tables from them. In that stored procedure, you could check to see if the object exists already as a table and skip that object.

Run stored procedure to populate an existing table from an MVC Controller

The following SQL code works perfectly in [QTY] database. it deletes all the rows in [Table1], then runs the stored procedure [test] and inserts the result into [Table1].
I want to be able to run this code from an MVC controller. How can I achieve this? Thank you.
USE [QTY]
GO
DECLARE #return_value int
Delete from Table1
INSERT INTO Table1
EXEC #return_value = [dbo].[test]
#Month = N'M4',
#Forecast = '2019-04-30'
SELECT 'Return Value' = #return_value
GO
Try to use FromSql method which enables you to pass in a SQL command to be executed against the database to return instances of the type represented by the DbSet .
// Format string
var author = db.Authors.FromSql("SELECT * From Authors Where AuthorId = {0}", id).FirstOrDefault();
// String interpolation
var author = db.Authors.FromSql($"SELECT * From Authors Where AuthorId = {id}").FirstOrDefault();
The DbContext exposes a Database property which includes a method called ExecuteSqlCommand. This method returns an integer specifying the number of rows affected by the SQL statement passed to it.
using(var context = new SampleContext())
{
var commandText = "INSERT Categories (CategoryName) VALUES (#CategoryName)";
var name = new SqlParameter("#CategoryName", "Test");
context.Database.ExecuteSqlCommand(commandText, name);
}
You could take some time on the tutorial Executing Raw SQL Queries

Execute DB_ID sql function from scala

I would like to call the DB_ID function of SQL Server to retrieve the databaseID of a user database from scala..
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver")
val connection: Connection = DriverManager.getConnection(jdbcConnectionString)
//SELECT database_id FROM sys.databases WHERE Name
val statement: CallableStatement = connection.prepareCall("{? =call DB_ID(?)}")
statement.registerOutParameter(1,java.sql.Types.INTEGER)
statement.setString(2,s"'ABC_STORE'")
statement.execute()
val a = statement.getInt(1)
I get an error COULD NOT FIND the stored procedure DB_ID.
How do i get this to work.
If you make statement.setString you don't need to use your String between the two '' so change :
statement.setString(2,s"'ABC_STORE'")
by
statement.setString(2, "ABC_STORE")
What the s do in 2,s"' make sure to remove it.
You can learn more here :
Using a Stored Procedure with a Return Status and Prepared Statement doc

JDBC Code Change From SQL Server to Oracle

In the JDBC code, I have the following that is working with SQL Server:
CallableStatement stmt = connection.prepareCall("{ call getName() }");
ResultSet rs = stmt.executeQuery();
if(rs != null)
{
while(rs.next())
{
//do something with rs.getString("name")
}
}
Multiple rows are returned for the above situation.
I understand that the use of a cursor is required to loop through the table in Oracle, but is there any way to keep the above code the same and accomplish the same thing?
Sample PL/SQL code would be much appreciated.
Thanks in advance.
You could implement getName() as a pipelined function:
CREATE OR REPLACE name_record AS OBJECT ( name VARCHAR2(100) );
/
CREATE OR REPLACE name_table AS TABLE OF name_record;
/
CREATE OR REPLACE FUNCTION getName RETURN name_table PIPELINED
AS
n name_record;
BEGIN
-- I have no idea what you're doing here to generate your list of names, so
-- I'll pretend it's a simple query
FOR i IN (SELECT name FROM someTable) LOOP
n := name_record( i.name );
PIPE ROW(n);
END LOOP;
END;
/
You would need to change the actual query in Java to SELECT name FROM TABLE(getName()).
This is straight JDBC, so it'll work with any database that has a valid JDBC driver.
It assumes, of course, that the stored proc exists in both and that you aren't using any non-standard, vendor-proprietary code in your class.

Resources