MySql syntax question regarding CONCAT and strings - stored-procedures

Environment is MySql 5.1.5.
This is a snippet from a larger stored procedure. Assume the variables are properly declared and set before this code is reached.
When I run the following loop, something seems to be failing in the CONCAT. #columnName and #xmlQuery are both VARCHARs. When I "SELECT #xmlQuery" at the end of the procedure, it is {null}.
If I simply replace:
SET #xmlQuery = CONCAT(#xmlQuery, #columnName);
with:
SET #xmlQuery = CONCAT(#xmlQuery, 'test');
then I get a nice string back like:
select xml_tag('result',null,null,concat(testtesttesttesttesttest
as one would expect.
WHY doesn't the CONCAT work with the local VARCHAR variable?
SET #xmlQuery = 'select xml_tag(''result'',null,null,concat(';
SET #columnCount = (SELECT COUNT(*) FROM ColumnNames);
WHILE (#rowIndex <= #columnCount) DO
SELECT #columnName = ColumnName FROM ColumnNames WHERE ID = #rowIndex;
SET #xmlQuery = CONCAT(#xmlQuery, #columnName);
SET #rowIndex = #rowIndex + 1;
END WHILE;

The problem turned out to be a conflict between the local variable #columnName and the column ColumnName in my temporary table.

Related

IBM i Access Client Solutions (ACS) Run SQL Scripts - print variable

is there an easy way to display/print a variable on the output of the IBM i ACS Run SQL Script screen?
For having a quick check when writing a script it would very helpful to be able to display the content of a variable. E.g. in below script, I want to know the value of "my_counter".
BEGIN
DECLARE my_counter INT;
SET my_counter = my_counter + (SELECT count(*) FROM QSYS2.OBJECT_LOCK_INFO WHERE SYSTEM_OBJECT_SCHEMA = 'ABC' AND SYSTEM_OBJECT_NAME = 'DEF' AND OBJECT_TYPE = '*FILE');
SET my_counter = my_counter + (SELECT count(*) FROM QSYS2.OBJECT_LOCK_INFO WHERE SYSTEM_OBJECT_SCHEMA = 'ABC' AND SYSTEM_OBJECT_NAME = 'XYZ' AND OBJECT_TYPE = '*FILE');
PRINT my_counter; --> I want to print the content of variable my_counter but PRINT is not a valid keyword (all other valid keywords have a blue color, this one does not).
END;
You probably also notice that I'm doing "my_counter = my_counter + ...", I tried using "my_counter += ..." but no such thing. Is there a better way of doing this?
Hellow KoenS,
This is how I do this:
create or replace variable #my_Counter integer default 0;
SET #my_counter = #my_counter + (SELECT count(*) FROM QSYS2.OBJECT_LOCK_INFO WHERE SYSTEM_OBJECT_SCHEMA = 'ABC' AND SYSTEM_OBJECT_NAME = 'ABC' AND OBJECT_TYPE = '*FILE');
SET #my_counter = #my_counter + (SELECT count(*) FROM QSYS2.OBJECT_LOCK_INFO WHERE SYSTEM_OBJECT_SCHEMA = 'XYZ' AND SYSTEM_OBJECT_NAME = 'XYZ' AND OBJECT_TYPE = '*FILE');
select #my_Counter from sysibm.sysdummy1;
drop variable #my_Counter;
Variables are CL SRVPGM that are located in a library, if you have problems with it, especify library (abc.#my_Counter or use SET SCHEMA and SET PATH comands before create and use the variable.
I use # before variables by convention.
To display a variable use sysibm.sysdummy1 file.

How to properly parameterize my postgresql query

I'm trying to parameterize my postgresql query in order to prevent SQL injection in my ruby on rails application. The SQL query will sum a different value in my table depending on the input.
Here is a simplified version of my function:
def self.calculate_value(value)
calculated_value = ""
if value == "quantity"
calculated_value = "COALESCE(sum(amount), 0)"
elsif value == "retail"
calculated_value = "COALESCE(sum(amount * price), 0)"
elsif value == "wholesale"
calculated_value = "COALESCE(sum(amount * cost), 0)"
end
query = <<-SQL
select CAST(? AS DOUBLE PRECISION) as ? from table1
SQL
return Table1.find_by_sql([query, calculated_value, value])
end
If I call calculate_value("retail"), it will execute the query like this:
select location, CAST('COALESCE(sum(amount * price), 0)' AS DOUBLE PRECISION) as 'retail' from table1 group by location
This results in an error. I want it to execute without the quotes like this:
select location, CAST(COALESCE(sum(amount * price), 0) AS DOUBLE PRECISION) as retail from table1 group by location
I understand that the addition of quotations is what prevents the sql injection but how would I prevent it in this case? What is the best way to handle this scenario?
EDIT: I added an extra column to be fetched from the table to highlight that I can't use pick to get one value.
find_by_sql is used when you want to populate objects with a single line of literal SQL. But we can use ActiveRecord for most of this, we just need one single column. To make objects, use select. If you just want results use pluck.
As you're picking from a fixed set of strings there's no risk of SQL injection in this code. Use Arel.sql to pass along a SQL literal you know is safe.
def self.calculate_value(result_name)
sum_sql = case result_name
when "quantity"
"sum(amount)"
when "retail"
"sum(amount * price)"
when "wholesale"
"sum(amount * cost)"
end
sum_sql = Arel.sql(
"coalesce(cast(#{sum_sql} as double precision), 0) as #{result_name}"
)
return Table1
.group(:location)
# replace pluck with select to get Table1 objects
.pluck(:location, sum_sql)
end

Assigning variable to Snowflake SQL based on condition

I am trying to execute a sql command based on input variable passed as parameter in Stored Procedure. If the parameter passed was INCR, I would like to append a part of the sql to final sql query variable.
var exec_cmnd = (load_type == 'INCR') ? "AND EXISTS (SELECT 1 FROM tbl T WHERE inner_tbl.id = outer_tbl.id)" : "1 = 1";
var qry = "`select * from tbl_a where name=name and `+exec_cmnd+`";
snowflake.execute({sqlText:qry});
But the condition seem to throw an error. Please let me know how i can assign a variable to a sql command query variable based on IF condition?
If load_type is passed into the stored procedure, you need to uppercase it in the body of the JavaScript.
https://docs.snowflake.com/en/sql-reference/stored-procedures-usage.html#case-sensitivity-in-javascript-arguments
Your concatenation is off slightly. I highly recommend using JavaScript literal templates for SQL in JavaScript stored procedures.
Open your string with a back tick and close with a back tick. You can then replace any JavaScript variable by wrapping it like this: ${myVariable}. It also allows you to use single and double quotes and multi-line statements in your SQL without issues.
create or replace procedure foo(load_type string)
returns string
language javascript
as
$$
var exec_cmnd = (LOAD_TYPE == 'INCR') ? "EXISTS (SELECT 1 FROM tbl T WHERE inner_tbl.id = outer_tbl.id)" : "1 = 1";
var qry = `select * from tbl_a where name=name and ${exec_cmnd}`;
return qry;
$$;
call foo('INCR');

informix update based on values in another table

I have the following select statement that works just fine:
select stock_master.stock_code, stk_stock_status, reorder_buyer, default_route_id, main_work_centre
from stock_master, bill_of_materials_header, production_routing
where stock_master.stock_code = bill_of_materials_header.stock_code
and bill_of_materials_header.default_route_id = production_routing.prh_route_id
and main_work_centre != "CNC";
and stock_group >= 3201 and stock_group <= 3299;
What I want to do is update the stk_stock_status to "M" for this condition, but can't seem to work out the correct syntax for the update command. Any pointers would be extremely helpful.
If stk_stock_status is instock_master table and this table has some ID column then convert your select into select that returns only identifiers which should be updated. It will look like:
UPDATE stock_master SET stk_stock_status = 'M' WHERE stk_stock_id IN
(
SELECT stk_stock_id
FROM stock_master, bill_of_materials_header, production_routing
WHERE stock_master.stock_code = bill_of_materials_header.stock_code
AND bill_of_materials_header.default_route_id = production_routing.prh_route_id
AND main_work_centre != 'CNC';
)
PS Do not use " for sting literals. It is allowed by Informix but not by SQL standard. Better use '.

T-SQL Matching process using only provided fields

I am trying to write a stored procedure to match lists of physicians with existing records in our database based off of the information provided to us by our clients. Currently we use MS Access to join manually based on the given identifiers, but this process tends to be tedious and overly time consuming, hence the desire to automate it.
What I am trying to do is create a temporary table that contains all columns that could potentially be matched on, and then run through a series of matching queries using the fields as join conditions to get our identifier to pass back.
For instance, the available matching fields are Name, NPI, MedicaidNum, and DOB so I would write something like:
UPDATE Temp
SET Temp.RECID = Phy.RECID
FROM TempTable Temp
INNER JOIN Physicians Phy
ON Phy.Name = Temp.Name
AND Phy.NPI = Temp.NPI
AND Phy.MedicaidNum = Temp.MedicaidNum
AND Phy.DOB = Temp.DOB
UPDATE Temp
SET Temp.RECID = Phy.RECID
FROM TempTable Temp
INNER JOIN Physicians Phy
ON Phy.Name = Temp.Name
AND Phy.NPI = Temp.NPI
AND Phy.MedicaidNum = Temp.MedicaidNum
WHERE Temp.RECID IS NULL
...etc
The problem lies in the fact that there about 15 different identifiers which could potentially be provided and clients usually only provide three or four per record set. So by the time null values are accounted for, there are potentially over a hundred different queries that need to be written to match on only half a dozen provided fields.
I am thinking that there may be a way to pass in a variable (or variables) which indicate which columns are actually provided with the data set, and then write a dynamic join statement and/or where clause, but I do not know if this will work in T-SQL. Something like:
DECLARE #Field1
DECLARE #Field2
....
UPDATE Temp
SET Temp.RECID = Phy.RECID
FROM TempTable Temp
INNER JOIN Physicians Phy
ON Phy.#Field1 = Temp.#Field1
AND Phy.#Field2 = Temp.#Field2
This way I would limit the number of queries I need to write, and only need to worry about the number of fields I am matching, rather then which specific ones. Perhaps there is a better approach to this problem however?
You can do something like this, but be warned this method is super prone to SQL injection. It's just to illustrate the principle of how to do something like this. I leave it up to you what you want to do with it. For this code, I made the proc take three fields:
CREATE PROC DynamicUpdateSQLFromFieldList #Field1 VARCHAR(50) = NULL,
#Field2 VARCHAR(50) = NULL,
#Field3 VARCHAR(50) = NULL,
#RunMe BIT = 0
AS
BEGIN
DECLARE #SQL AS VARCHAR(1000);
SELECT #SQL = 'UPDATE Temp
SET Temp.RECID = Phy.RECID
FROM TempTable Temp
INNER JOIN Physicians Phy ON ' +
COALESCE('Phy.' + #Field1 + ' = Temp.' + #Field1 + ' AND ', '') +
COALESCE('Phy.' + #Field2 + ' = Temp.' + #Field2 + ' AND ', '') +
COALESCE('Phy.' + #Field3 + ' = Temp.' + #Field3, '') + ';';
IF #RunMe = 0
SELECT #SQL AS SQL;
ELSE
EXEC(#SQL)
END
I've added a debug mode flag just so you can see the SQL if you don't want to run it. So, for example, if you run:
EXEC DynamicUpdateSQLFromFieldList #field1='col1', #field2='col2', #field3='col3'
or
EXEC DynamicUpdateSQLFromFieldList #field1='col1', #field2='col2', #field3='col3', #RunMe=0
the SQL produced will be:
UPDATE Temp
SET Temp.RECID = Phy.RECID
FROM TempTable Temp INNER JOIN Physicians Phy
ON Phy.col1 = Temp.col1 AND
Phy.col2 = Temp.col2 AND
Phy.col3 = Temp.col3;
If you run this line:
EXEC DynamicUpdateSQLFromFieldList #field1='col1', #field2='col2', #field3='col3', #RunMe=1
It will perform the update. If you wanted it to be more secure, you could whitelist the incoming field names against the sys tables to make sure the columns actually exist in each table before you execute any code.

Resources