How to call plpgsql functions from Ruby on Rails application?
This is the function definition:
CREATE OR REPLACE FUNCTION add_org_pincode(i_user_id integer, i_org_id integer, i_amount numeric, i_pincode_id integer)
RETURNS integer AS
$BODY$
Declare
v_org_balance numeric;
Begin
Select o.balance
Into Strict v_org_balance
From organizations o
Left Join organizations d On o.parent_organization_id = d.id
Where o.id = i_org_id
For Update Of o;
--
Insert Into org_balance_transactions(organization_id, balance, amount_added, reason, action_user_id, transaction_id)
Values (i_org_id, v_org_balance, i_amount, 10, i_user_id, i_pincode_id);
--
Update organizations
Set balance = balance + i_amount
Where id = i_org_id;
-- Other error
Return 0;
End;$BODY$
LANGUAGE 'plpgsql' VOLATILE;
So, how do I call add_org_pincode?
Like the others have said, something akin to:
result = connection.execute ("SELECT add_org_pincode(#{user_id}, #{org_id}, #{amount}, #{pincode_id};")
This is an example that I have proven, it might help you...
CREATE LANGUAGE plpgsql;
/**PROMEDIOS**/
CREATE OR REPLACE FUNCTION average(valores NUMERIC[],OUT prom NUMERIC) AS $$
DECLARE
element_count INT4;
sum numeric := 0.0;
BEGIN
element_count := array_upper(valores, 1) - array_lower(valores, 1) +1;
FOR i IN array_lower(valores, 1) .. array_upper(valores, 1)
LOOP
sum := sum + valores[i];
END LOOP;
prom := trunc(sum/element_count,1);
END;
$$ LANGUAGE plpgsql;
Obviously you have to execute this SQL in your database, just add it in your SQL Editor (pointing your database) in PGAdmin, after executing that, this function must be available like this select average(ARRAY[4.5,3.2,7.0]);.
In Rails Console I tried the example like this:
_calificaciones = Calificacion.all(:conditions => ["promedio_parciales != average(array[parcial1,parcial2,parcial3])"])
and it worked fine for me... Good luck...
You can go straight to the ActiveRecord::Base.connection to execute SQL which is what I did for some prototype code...
result = ActiveRecord::Base.connection.execute("SELECT * FROM stock_hourly_performance(#{stock_id})")
I don't know if its the best solution to the problem but it got me up and running.
You can execute raw SQL with:
ActiveRecord::Base.connection.execute('SQL goes here')
Be sure that it is absolutely necessary to bypass ActiveRecord (Rail's ORM).
Related
CREATE PROCEDURE EPS.PROCEDURE_OTE_LTE_BIDDER_REPORT
(
IN P_USERID INTEGER,
IN P_AUCTIONID INTEGER,
IN P_REPORT_FLAG VARCHAR(3),
OUT O_ERROR_CODE INTEGER,
OUT OUTPUT_MESSAGE VARCHAR(100),
IN P_LOG_USERID INTEGER
)
LANGUAGE SQL
P1:BEGIN ATOMIC DECLARE SQLCODE INTEGER DEFAULT 0;
DECLARE V_USERID INTEGER;
DECLARE V_AUCTIONID INTEGER;
DECLARE V_REPORT_FLAG_TECHNOCOMMERCIALQUALIFIEDCUSTOMER VARCHAR(3);
DECLARE V_COUNT_TECHNOCOMMERCIALQUALIFIEDCUSTOMER INTEGER;
DECLARE V_REPORT_FLAG_SELECTIVEUSERWISETENDERREPORT VARCHAR(3);
DECLARE V_COUNT_SELECTIVEUSERWISETENDERREPORT INTEGER;
SELECT COUNT(*) INTO V_COUNT_TECHNOCOMMERCIALQUALIFIEDCUSTOMER FROM EPS.TECHNOCOMMERCIALQUALIFIEDCUSTOMER A
WHERE A.AUCTIONID=P_AUCTIONID AND A.USERID=P_USERID;
SELECT COUNT(*) INTO V_COUNT_SELECTIVEUSERWISETENDERREPORT FROM EPS.SELECTIVEUSERWISETENDERREPORT B
WHERE B.AUCTIONID=P_AUCTIONID AND B.USERID=P_USERID;
IF P_REPORT_FLAG = 'Y' THEN
IF V_COUNT_TECHNOCOMMERCIALQUALIFIEDCUSTOMER < 1 THEN
INSERT INTO EPS.TECHNOCOMMERCIALQUALIFIEDCUSTOMER (A.AUCTIONID,A.USERID,A.QUALIFIED) VALUES (P_AUCTIONID,P_USERID,'Y');
ELSE
SET OUTPUT_MESSEGE = 'DATA ALREADY PRESENT';
END IF;
ELSE
IF V_COUNT_TECHNOCOMMERCIALQUALIFIEDCUSTOMER > 0 THEN
DELETE FROM EPS.TECHNOCOMMERCIALQUALIFIEDCUSTOMER C WHERE C.AUCTIONID=P_AUCTIONID AND C.USERID=P_USERID;
ELSE
SET OUTPUT_MESSAGE = 'NO DATA FOUND';
END IF;
END IF;
IF P_REPORT_FLAG = 'Y' THEN
IF V_COUNT_SELECTIVEUSERWISETENDERREPORT < 1 THEN
INSERT INTO EPS.SELECTIVEUSERWISETENDERREPORT AA
( AA.AUCTIONID,
AA.USERID,
AA.TENDERREPORTTYPEID,
AA.STATUS,
AA.CREATEID,
AA.CREATEDATE,
AA.UPDATEID,
AA.UPDATEDATE
)
VALUES
(
P_AUCTIONID,
P_USERID,
103.
'A',
P_LOG_USERID,
CURRENT TIMESTAMP,
NULL,
NULL
);
ELSE
SET OUTPUT_MESSEGE = 'DATA ALREADY PRESENT';
END IF;
ELSE
IF V_COUNT_SELECTIVEUSERWISETENDERREPORT > 0 THEN
DELETE FROM EPS.SELECTIVEUSERWISETENDERREPORT CC WHERE CC.AUCTIONID=P_AUCTIONID AND CC.USERID=P_USERID;
ELSE
SET OUTPUT_MESSAGE = 'NO DATA FOUND';
END IF;
END IF;
END P1
I am getting this error
SQL Error [42601]: An unexpected token "END-OF-STATEMENT" was found following "Y PRESENT'". Expected tokens may include: "
END IF".. SQLCODE=-104, SQLSTATE=42601, DRIVER=4.7.85
SQL Error [42601]: An unexpected token "END-OF-STATEMENT" was found following "Y PRESENT'". Expected tokens may include: "
END IF".. SQLCODE=-104, SQLSTATE=42601, DRIVER=4.7.85
An unexpected token "END-OF-STATEMENT" was found following "Y PRESENT'". Expected tokens may include: "
END IF".. SQLCODE=-104, SQLSTATE=42601, DRIVER=4.7.85
An unexpected token "END-OF-STATEMENT" was found following "Y PRESENT'". Expected tokens may include: "
END IF".. SQLCODE=-104, SQLSTATE=42601, DRIVER=4.7.85
It helps to properly use a syntax editor that understands SQL, and take more care with checking your code. A good SQL editor may highlight your mistakes before you try to compile, as would any code review.
On a separate note, you should understand the difference between ANSI SQL PL and Oracle PL/SQL. Your code seems to use ANSI SQL PL syntax, although your mistakes may be mistakes for any flavour of SQL.
Here are some of the obvious syntax mistakes in your code (there may be others):
On the line INSERT INTO EPS.SELECTIVEUSERWISETENDERREPORT AA , the AA should be omitted.
For the same insert statement you have 103., when you might mean 103,.
For the line INSERT INTO EPS.TECHNOCOMMERCIALQUALIFIEDCUSTOMER (A.AUCTIONID,A.USERID,A.QUALIFIED) you probably mean instead
INSERT INTO EPS.TECHNOCOMMERCIALQUALIFIEDCUSTOMER (AUCTIONID,USERID,QUALIFIED)
The same mistake is present for the line with INSERT INTO EPS.SELECTIVEUSERWISETENDERREPORT (do not qualify the column names).
For the line beginning SET OUTPUT_MESSEGE = you probably mean SET OUTPUT_MESSAGE =, and this typo is present on other lines.
The requirement is to read the values from one flat file and use them in for loop to modify the pgSQL queries.
I'm new to PostgreSQL. Please suggest any.
CREATE FUNCTION file_vals()
RETURNS varchar[] AS
$BODY$
return (readLines('my_file.txt'))
$BODY$
language plpgsql;
DO $ANONYMOUS_BLOCK$ declare
/* data */
begin
for c, l in user_c, file_vals()
loop
perform x.y(user => c.user, password => l);
RAISE NOTICE '%', c.user;
end loop;
end $ANONYMOUS_BLOCK$;
I'm trying to read the values from my_file.txt and also use the values in for loop to insert the values in database(x.y is the psql function) .
my_file.txt contains passwords:
a
b
c
d
user_c is cursor, which is having user values.
Need to use both the values i.e, user_c and file_vals in one single for loop statement.
Error:
psql:test.psql:6: ERROR: syntax error at or near "return"
LINE 4: return (readLines('my_file.txt'))
^
SET
psql:test.psql:20: ERROR: "c" is not a known variable
LINE 7: for c, l in user_c, file_vals()
I have been trying to figure out how to make the postcondition work for the following code.
There are 3 classes, Bank is the client of Customer, and Customer is the client of Account
Here is the Bank Class, I just cant pass the postcondition other_customer_unchanged
new (name1: STRING)
-- Add a new customer named 'name1'
-- to the end of list `customers'
require
customer_not_already_present:customer_exists(name1)=false
do
customers.force (create {CUSTOMER}.make (name1))
count := customers.count
ensure
total_balance_unchanged:
sum_of_all_balances = old sum_of_all_balances
num_customers_increased:count /= old count and old count+1=count
total_unchanged:total = old total
customer_added_to_list:
customer_exists (name1)
and then customers[customer_id (name1)].name ~ name1
and then customers[customer_id (name1)].balance ~ zero
other_customers_unchanged:
customers_unchanged_other_than(name1, old customers.deep_twin)
end
Here is the feature of customers_unchanged_other_than
customers_unchanged_other_than (a_name: STRING;old_customers:like customers): BOOLEAN
-- Are customers other than `a_name' unchanged?
local
c_name: STRING
do
from
Result := true
customers.start
until
customers.after or not Result
loop
c_name := customers.item.name
if c_name /~ a_name then
Result := Result and then
old_customers.has (customers.item)
end
customers.forth
end
ensure
Result =
across
customers as c
all
c.item.name /~ a_name IMPLIES
old_customers.has (c.item)
end
end
and I have redefined the is_equal feature in customer class
is_equal (other: like Current): BOOLEAN
do
Result := name ~ other.name and balance = other.balance
ensure then
Result = (name ~ other.name and balance = other.balance)
end
I have looked into what is in the old customer.deep_twin, it does contain the item of the customer, but somehow when it uses .has feature, it just makes the Result false.
Any help is greatly appreciated :)
I presume from your code that customers and old_customers are of type descendant of CONTAINER (ARRAY, LIST, STACK, QUEUE, etc.) You can then use customers.compare_objects (or old_customers.compare_objects) to ask the CONTAINER to use is_equal instead of "=" when searching.
DB2 V9 Z/Os
CREATE PROCEDURE SERDB.I21MMSNOUPD ()
RESULT SETS 1
LANGUAGE SQL
FENCED
COLLID SER
WLM ENVIRONMENT DDSNSPENV
RUN OPTIONS 'NOTEST(NONE,*,*,*)'
P1: BEGIN
--Declare variables
DECLARE CONSUMER INTEGER;
DECLARE NEW_MMS_NO INTEGER;
DECLARE END_TABLE INT DEFAULT 0;
DECLARE C1 CURSOR FOR
SELECT I20_CONSUMER_ID,
NEW_MMS_NO
FROM SERDB.I20_TEMP
-- WHERE I20_CONSUMER_ID = 164921;
ORDER BY I20_CONSUMER_ID;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET END_TABLE = 1;
OPEN C1;
FETCH C1 INTO CONSUMER,
NEW_MMS_NO;
WHILE END_TABLE = 0 DO
UPDATE SERDB.I20_CONSUMER_T
SET I20_MMS_NO = NEW_MMS_NO
WHERE I20_CONSUMER_ID = CONSUMER;
END WHILE;
CLOSE C1;
END P1
The above stored procedure builds with a cond code 0, but fails to execute even when a specific consumer_id. Does anyone see something wrong?
Individual sql statements run exactly as they're supposed to.
I've followed the examples for Cursors in SQL Procedures from IBM.
Thank you
I agree 100% with #X-Zero, this seems like a huge amount of work defining cursors and what-not, when you could do a simple set-based operation (likely with better performance). Here are two examples of how you can do it with a single operation:
Normal UPDATE:
UPDATE SESSION.I20_CONSUMER_T A
SET I20_MMS_NO = (
SELECT NEW_MMS_NO
FROM SESSION.I20_TEMP B
WHERE A.I20_CONSUMER_ID = B.CONSUMER
)
WHERE EXISTS (
SELECT 1
FROM SESSION.I20_TEMP C
WHERE A.I20_CONSUMER_ID = C.CONSUMER
)
New MERGE hotness:
MERGE INTO SESSION.I20_CONSUMER_T AS T
USING SESSION.I20_TEMP AS M
ON T.I20_CONSUMER_ID = M.CONSUMER
WHEN MATCHED THEN
UPDATE SET T.I20_MMS_NO = M.NEW_MMS_NO
ELSE IGNORE
These were tested on DB2 for Linux/Unix/Windows v9.7, but should work on any version of DB2 newer than 9.1 (DB2 for iSeries is a wildcard, I never remember what that platform does or doesn't support :) )
The FETCH command must be inside the WHILE, so that each time it is invoked, it fetches a row.
I have the following code:
delimiter ;
DROP PROCEDURE IF EXISTS ufk_test;
delimiter //
CREATE PROCEDURE ufk_test(IN highscoreChallengeId INT UNSIGNED)
BEGIN
DECLARE vLoopOrder INT UNSIGNED DEFAULT 5;
DECLARE vLoopLimit INT UNSIGNED DEFAULT 10;
select * from fb_user LIMIT vLoopOrder,vLoopLimit;
END//
delimiter ;
Mysql returns the following error:
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'vLoopOrder,vLoopLimit;
END' at line 11
it seems that I cannot use declared variables in a LIMIT statement. is there any other way to overcome this ?
of course this is a simple example, here i could just put static numbers but I need to know if it's possible in any way to use any kind of variables with LIMIT.
Thanks
i use something like:
SET #s = CONCAT('SELECT * FROM table limit ', vLoopOrder ', ', vLoopLimit);
PREPARE stmt1 FROM #s;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;