I am performing stored procedures the input option is as follows:
Declare #table varchar (20)
Set #query = 'Select * from' + #table + 'Where id = 1'
Exec sp_executesql #query
The question is can it be done in some other way without the SQL being handled as varchar?
I was thinking along the lines of something similar to the following:
Declare #varchar varchar(20)
Set #tabla = 'MyTabla'
SELECT * FROM #table
Related
I want to use constant of an array in SQL heredoc like:
ROLES = ['admin', 'staff', 'guest']
query = <<-SQL
SELECT * FROM users
WHERE role IN (#{ROLES})
SQL
ActiveRecord::Base.connection.execute(query)
but getting syntax error
ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: syntax error at or near "["
LINE 2: WHERE role IN (["admin", "staff", "guest"])
^
Instead of building your own Ruby to SQL converter you could also make use of the arel gem which already does the necessary escaping and quoting.
users = User.arel_table
ApplicationRecord.connection.execute(<<~SQL.squish)
SELECT *
FROM users
WHERE #{users[:role].in(ROLES).to_sql}
SQL
Or if you'd like to go full arel.
ApplicationRecord.connection.execute(
users.project(users[Arel.star])
.where(users[:role].in(ROLES))
.to_sql
)
Instead of passing the result of ROLES.inspect, you should make the string with roles yourself, I would use map and join for that purpose:
query = <<-SQL
SELECT * FROM users
WHERE role IN (#{ROLES.map { |role| "'#{role}'" }.join(',')})
SQL
ApplicationRecord.connection.execute(query)
I am looking to re-write a legacy Sybase stored procedure that's 20 years old.
The logic seems pretty simple (an UPSERT of a single row into a table, with the only wrinkle being that unique key for table lookups can be 2 or 3 columns, based on input parameter).
The problem is, the stored procedure accomplishes this by declaring a cursor and using that instead of simply doing an UPSERT (in 2 versions - 2/3 column ones - based on an IF statement).
Is there any reason to use the cursor? Or was this just the original long-gone-developer being too fancy for no good reason?
In case it matters, the details:
Table has key-value pairs, stored for multiple entities. Each key can either be multi-valued or single-valued (which is in a lookup key list table, and is outside the scope of this question). Each record has an active flag:
entity_id int -- foreign key into entity table
unique_id int -- identity column for this table
key varchar(20)
value varchar(255)
active char
comment varchar(20) -- Change control audit purposes.
Stored procedure accepts #entity_id, #key, #value, #active and #comment as input parameters.
Unique key for the table is:
entity_id + key, for keys that are single-valued
entity_id + key + value, for keys that are multiple-valued
The stored procedure does the upsert - if the table already has the row as defined by the unique key, it updates the active flag (and the setting value, for single-valued keys).
If the table does not have the row as defined by unique key, it gets inserted.
The way I would do it would be simple
DECLARE #unique_id int
IF (#key_type="M") -- multi-valued
BEGIN
SELECT #unique_id = unique_id
FROM my_table
WHERE entity_id = #entity_id
AND key = #key
AND value = #value
IF (#unique_id IS NULL) BEGIN
-- INSERT
END
ELSE BEGIN
UPDATE my_table
SET active = #active
,comment = #comment
WHERE entity_id = #entity_id
AND key = #key
AND value = #value
END
END
ELSE -- single-valued
BEGIN
SELECT #unique_id = unique_id
FROM my_table
WHERE entity_id = #entity_id
AND key = #key
IF (#unique_id IS NULL) BEGIN
-- INSERT
END
ELSE BEGIN
UPDATE my_table
SET active = #active
,comment = #comment
,value = #value
WHERE entity_id = #entity_id
AND key = #key
END
END
Instead, the stored procedure does this:
IF (#key_type="M") BEGIN -- multi-valued
DECLARE my_cursor CURSOR
SELECT active FROM my_table
WHERE entity_id = #entity_id
AND key = #key
-- next line is the ONLY difference between single and multiple value cursor
AND value = #value
FOR UPDATE OF value, active, comment
END
ELSE BEGIN -- Single-valued
DECLARE preexisting CURSOR
SELECT active FROM my_table
WHERE entity_id = #entity_id
AND key = #key
FOR UPDATE OF value, active, comment
END
And then does upsert using the cursor:
open my_cursor
DECLARE #cactive CHAR
FETCH my_cursor INTO #cactive
if ( ##sqlstatus = 0 )
UPDATE my_table
SET active = #active
,comment = #comment
,value = #value
WHERE current of my_cursor
ELSE
-- INSERT (no cursor use, same insert I would do)
close my_cursor
deallocate cursor my_cursor
DB - PostgreSQL
Rails - 4
I have next SQL
sql = <<-SQL
LEFT OUTER JOIN (
SELECT SUM(id) AS sum_ids, some_key FROM second_models
WHERE id < 10000
GROUP BY some_key
) AS second_models ON first_models.id = second_models.first_model_id
SQL
record = FModel.joins(sql).last
record.sum_ids # DOESN'T WORK !
and I can see the record as ActiveRecord object, but can I get somehow field sum_ids which was built manually?
The additional field is inside the join section. It is not selected by default and thus can't be read. When executing your statement you get something like the following SQL query:
SELECT first_models.*
FROM first_models
INNER JOIN (
SELECT SUM(id) AS sum_ids, some_key
FROM second_models
WHERE id < 10000
GROUP BY some_key
) AS second_models
ON first_models.id = second_models.first_model_id
The first select statement prevents the sum_ids field from being accessible in your object since it's never returned to Rails. You want to change SELECT first_models.* to SELECT *. This is simply done by specifying the following select:
record = FModel.select(Arel.star).joins(sql).last
record.sum_ids
#=> should now give you your value
You can also add your field specifically using the following method:
f_models = FModel.arel_table
record = FModel.select(f_models[Arel.star]).select('sum_ids').joins(sql).last
This should result in SELECT first_models.*, sum_ids.
I have a Stored Procedure that outputs totals from an eForm table. I want to use this Stored Procedure to pull out data from other eForm tables and so want to replace the eForm table name in the From clause with a parameter. I've read the other questions, but can't find one that helps with using a parameter in From. The code for this Stored Procedure looks like this -
IF OBJECT_ID ('sp_SelectEformMonthlyTotals') IS NOT NULL
DROP PROCEDURE sp_SelectEformMonthlyTotals
GO
CREATE PROCEDURE sp_SelectEformMonthlyTotals #EFORMNAME VARCHAR (200) = ''
AS
CREATE TABLE #TEMP (EFORMNAME VARCHAR (200))
INSERT INTO #TEMP
EXEC sp_UpdateEForm #EFORMNAME
SELECT ....
FROM #EFORMNAME T1 INNER JOIN FLODS_CASE_ENQUIRY_F00 T2 ON T1.EFORM_CASE_ENQUIRY_ID = T2.CASE_ENQUIRY_ID
WHERE T2.CREATED_DT >= '2012-04-02'
GROUP BY MONTH(T2.CREATED_DT), YEAR(T2.CREATED_DT)) T3
ORDER BY YEAR, MONTH_NUMBER
DROP TABLE #TEMP
This is the latest attempt of many and now includes a second Stored Procedure called sp_UpdateEForm -
IF OBJECT_ID ('sp_UpdateEForm') IS NOT NULL
DROP PROCEDURE sp_UpdateEForm
GO
CREATE PROCEDURE sp_UpdateEForm #EFORMNAME VARCHAR (200) = ''
AS
UPDATE FRANCIS
SET eFormName = #EFORMNAME
I created the Francis table to hold the eFormName. I can see that I need to set the value of #EFORMNAME before sp_SelectEformMonthlyTotals can complete, but I just can't seem to find a way of doing this.
Thanks.
Frank
You only need to use a dynamic query for this;
CREATE PROCEDURE sp_SelectEformMonthlyTotals #EFORMNAME VARCHAR (200) = ''
AS
CREATE TABLE #TEMP (EFORMNAME VARCHAR (200))
INSERT INTO #TEMP
EXEC sp_UpdateEForm #EFORMNAME
DECLARE #sSQL AS VARCHAR(2000), #DateCreated as varchar(10)
SET #DateCreated = '2012-04-02'
SET #sSQL = 'SELECT .... '
SET #sSQL = #sSQL + ' FROM ' + #EFORMNAME + ' T1 INNER JOIN FLODS_CASE_ENQUIRY_F00 T2 ON T1.EFORM_CASE_ENQUIRY_ID = T2.CASE_ENQUIRY_ID '
SET #sSQL = #sSQL + ' WHERE T2.CREATED_DT >= '' + #DateCreated + '''
SET #sSQL = #sSQL + ' GROUP BY MONTH(T2.CREATED_DT), YEAR(T2.CREATED_DT)) T3'
SET #sSQL = #sSQL + ' ORDER BY YEAR, MONTH_NUMBER'
exec #sSQL
DROP TABLE #TEMP
Is there a way, in SQL Server, to declare a table variable without knowing the table definitions?
Exempli gratia:
DECLARE #Results TABLE
INSERT INTO #Results EXEC MyProc #param1 = #myValue
or
DECLARE #Results TABLE
SELECT INTO #Results EXEC MyProc #param1 = #myValue
or
DECLARE #Results TABLE
EXEC MyProc #param1 = #myValue INTO #Results
or
DECLARE #Results TABLE
EXEC INTO #Results MyProc #param1 = #myValue
or
DECLARE #Results TABLE
SELECT * FROM EXEC MyProc #param1 = #myValue INTO #Results
or
DECLARE #Results TABLE
SELECT * INTO #Results FROM EXEC MyProc #param1 = #myValue
or
DECLARE #Results TABLE
SELECT * INTO #Results EXEC MyProc #param1 = #myValue
(you get the idea)
Impossible. Citation from "books online":
==============
Syntax
Note Use DECLARE #local_variable to declare variables of type table.
table_type_definition ::=
TABLE ( { column_definition | table_constraint } [ ,...n ] )
==============
"(", at least one column definition and ")" is syntactically required.
PS: AFAIK insertion into any new table from "exec" results are impossible at all. Only to a table with predefined structre.
You can't do it with table VARIABLES but you can do it with TEMP tables.
-- Drop the table, if it exists
IF OBJECT_ID(N'tempdb.dbo.#tmpMyTable',N'U') IS NOT NULL
DROP TABLE #tmpMyTable
SELECT
ColumnA,
ColumnB
INTO #tmpMyTable
FROM MyTable
-- Then clean up after yourself
DROP TABLE #tmpMyTable