Rails more statements with ; doesnt work... :s - sql

I have this code, but i cant make it work:
images = Image.find_by_sql('PREPARE stmt FROM \' SELECT * FROM images AS i WHERE i.on_id = 1 AND i.on_type = "profile" ORDER BY i.updated_at LIMIT ?, 6\'; SET #lower_limit := ((5 DIV 6) * 6); EXECUTE stmt USING #lower_limit;')
I got this error:
Mysql::Error: 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 'SET #lower_limit := ((5 DIV 6) * 6); EXECUTE stmt USING #lower_limit' at line 1: PREPARE stmt FROM ' SELECT * FROM images AS i WHERE i.on_id = 1 AND i.on_type = "profile" ORDER BY i.updated_at LIMIT ?, 6'; SET #lower_limit := ((5 DIV 6) * 6); EXECUTE stmt USING #lower_limit;
but if i use a sql app, like this, it works:
PREPARE stmt FROM ' SELECT * FROM images AS i WHERE i.on_id = 1 AND i.on_type = "profile" ORDER BY i.updated_at LIMIT ?, 6'; SET #lower_limit := ((5 DIV 6) * 6); EXECUTE stmt USING #lower_limit;
SOLVED:
This generates 2 queries and is worse, but now i can get the offset of the image.
The other way around was with just one query, but i would not got any offset of the image, and anyway I couldn't make it work.
def self.get_image_offset(id)
image_offset = Image.find_by_sql("SELECT COUNT(id) as pos FROM images WHERE updated_at <= (SELECT updated_at FROM images WHERE id = #{id})")[0].pos.to_i
end
def self.get_group_offset(id, per_block, image_offset = nil)
image_offset ||= Image.get_image_offset(id)
group_offset = (image_offset / per_block).floor * per_block
{:image_offset => image_offset, :group_offset => group_offset, :group_number => ( group_offset + per_block ) / per_block}
end

You will be better off using something like execute [1] if you are writing the whole sql yourself (which is not really the 'Rails way', but that's a whole other story all together)
[1] http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#M001934

Related

FireDAC: possibility to make query faster - Delphi

I use bind variables in Delphi and on the other side there is Oracle database with Database link (#dblink). When I build a SELECT statement without bind variables, it has no problem with performance. Only if I query SELECT statement with bind variables it takes very long (sometimes 1-2 hour). Is it possible in FireDAC to make the query faster without changing SQL-query? I need to use bind variables to avoid SQL injection.
This SQL is very Fast in Delphi:
SELECT
DLGH_START_D Datum,
TRUNC((d.DLGH_ENDE_D - d.DLGH_START_D) * 24 * 60)||' Min '||
TRUNC(MOD(((d.DLGH_ENDE_D - d.DLGH_START_D) * 24 * 3600), 60))||' Sek' Dauer
FROM
dialoghistory d
WHERE
d.DLGH_PARAMETER_C = 'Name of Parameter' AND <--
d.dlgh_funktion_c = 'SQLS' AND
d.DLGH_START_D > '01.02.2020' <--
order by 1
This SQL is very slow in Delphi:
SELECT
DLGH_START_D Datum,
TRUNC((d.DLGH_ENDE_D - d.DLGH_START_D) * 24 * 60)||' Min '||
TRUNC(MOD(((d.DLGH_ENDE_D - d.DLGH_START_D) * 24 * 3600), 60))||' Sek' Dauer
FROM
dialoghistory d
WHERE
d.DLGH_PARAMETER_C = :B_Name AND <--
d.dlgh_funktion_c = 'SQLS' AND
d.DLGH_START_D > :Datum <--
order by 1
---------------------------
//slow execution period , because of bind variables (1 h)
qry := TFDQuery.CreateSQL(Application, sSqlText_.Text);
with qry do begin
...
Param.AsString := value; //set value of bind variable
...
Open;
Table dialoghistory looks like this
My Solution:
tmpQuery := TFDQuery.Create(self); // Create Query
tmpQuery.Sql.Text := 'SELECT * FROM dlgHistory d where d.DLGH_PARAMETER = :par';
...
// Set Data Type, Param Type and Size yourself
with tmpQuery.Params do begin
Clear;
with Add do begin
Name := 'par';
DataType := ftString;
Size := 128;
ParamType := ptInput;
end;
end;
tmpQuery.Params[0].AsString := 'Value'; //assign a value
tmpQuery.Prepare;
tmpQuery.Open; //And it works perfect!

Cursor in Oracle - after passing parameters, select does not filter result rows

I am facing for me strange issue with following parametrical cursor.
I have defined cursor in this way:
CURSOR cur_action ( product_code VARCHAR2(100) , action_master_list VARCHAR2(100))
IS
SELECT
act.ACTION_DETAIL_KEY,
act.ACTION_MASTER_KEY,
act.PRODUCT_CODE,
act.REF_ACTION_DETAIL_KEY
FROM XMLTABLE(action_master_list) x
JOIN ETDW.MFE_AR_ACTION_DETAILS act ON TO_NUMBER(x.COLUMN_VALUE) = act.ACTION_MASTER_KEY
WHERE 1=1
AND act.LAST_FLAG = 'Y'
AND act.PRODUCT_CODE = product_code;
Then I am using it in following way:
OPEN cur_action ( iFromProductCode , iActionMasterKeyList);
LOOP
FETCH cur_action BULK COLLECT INTO vActionDetailKey, vActionMasterKey, vProductCode, vRefActionDetailKey LIMIT 100;
FOR j IN 1..cur_action%ROWCOUNT
LOOP
dbms_output.put_line('vActionDetailKey: ' || vActionDetailKey (j) ||' vActionMasterKey: '|| vActionMasterKey (j) || ' vProductCode: ' || vProductCode (j));
END LOOP;
END LOOP;
Result seems to be unfilterd. It doesnt return 3 rows as expected result (this result is returned in with cusor query, when i run in outside procedure/pl block), but it returns all rows for actions in list. So it seems, that WHERE condition "act.PRODUCT_CODE = product_code" was not applied. Why?
Thank you
Why? Because you named parameter the same as column, so Oracle reads it as if it was where 1 = 1, i.e. no filtering at all.
Rename parameters to e.g.
CURSOR cur_action ( par_product_code V
----
this
and, later,
AND act.PRODUCT_CODE = par_product_code;
----
this

Create a BigQuery VIEW for each result in query

I created this query:
SELECT
(SELECT value from UNNEST(project.labels) where key = "project") as project,
ROUND(SUM(cost), 2) as cost
FROM cloud.dataset.billing_export
group by ar
And it returns me something like:
Row | project | cost
1 | PJ1 | 23
2 | PJ2 | 50
Is there a way to create a VIEW for each value (each project)?
I'm trying with UDF and each view must have a name based on the project (eg: view_PJ1) and got something like (but with a lot of errors):
LOOP
SET vars = (SELECT (SELECT value FROM UNNEST(project.labels) WHERE key = "project") AS project
FROM FROM cloud.dataset.billing_export
GROUP BY project);
IF vars=null THEN
LEAVE;
END IF;
CREATE OR REPLACE VIEW `cloud`.`dataset`.AR
AS
SELECT DISTINCT
(SELECT value from UNNEST(project.labels) where key = "project") as project,
ROUND(SUM(cost), 2) as cost
FROM cloud.dataset.billing_export
WHERE project=vars
GROUP BY project;
END LOOP;
Thanks in advance
This is a script that runs inside BigQuery that generates 4 views - giving each view a name coming out of a SQL query:
DECLARE x INT64 DEFAULT 0;
DECLARE rs ARRAY<STRING>;
SET rs = (
WITH data AS (SELECT i FROM `fh-bigquery.public_dump.numbers_255` WHERE i < 4)
SELECT ARRAY_AGG(
'CREATE OR REPLACE VIEW `temp.number' || i
||'` AS SELECT i FROM `fh-bigquery.public_dump.numbers_255` WHERE i=' || i
)
FROM data
);
LOOP
EXECUTE IMMEDIATE(SELECT rs[OFFSET(x)]);
SET x = x + 1;
IF x >= ARRAY_LENGTH(rs) THEN
LEAVE;
END IF;
END LOOP;
The secret is to use EXECUTE IMMEDIATE with a generated string creating the view.

PLSQL syntax error near ";"

I am trying to run a query for liquibase update and I have a PLSQL block in there, the block has over 500 lines so I only post a few lines where the error is happening for now.
BEGIN
IF NOT EXISTS(select 1 from "public"."eod_report" where "id" = NEW."id") THEN
-- LOADING STRUCTURE SECTION
select "id" into NEW.organization_unit_id from organization_unit where site_id = NEW.organization_unit_id;
orgUnitId := CAST(NEW.organization_unit_id as int8);
--INSERT
IF (NEW.transactions_count is NULL) THEN
NEW.transactions_count := 0;
END IF;
IF (NEW.total_sales is NULL) THEN
NEW.total_sales := 0;
END IF;
INSERT INTO "public"."eod_report"("id",batch,total_sales,transactions_count,organization_unit_id,pos_total_sales,pos_transactions_count,pos_total_points,total_points,transaction_date)
VALUES (NEW."id",NEW.batch,NEW.total_sales,NEW.transactions_count,orgUnitId,NEW.pos_total_sales,NEW.pos_transactions_count,NEW.pos_total_points,NEW.total_points,NEW.transaction_date);
-- updates delay transaction or each batch
select max(id) into lastEodId from "public"."transaction" where transaction_type = 4 and org_unit_id = orgUnitId and id &lt ;NEW."id";
for eodRow IN
select count(case when transaction_type = 3 then -1 else 1 end) as trCount,sum(case when report_prefix = true then points else -points end) as ptSum,sum(case when report_prefix = true then amount else -amount end) as trSum, batch as trBatch from "public"."transaction"
where id &gt ;lastEodId and id &lt ;NEW."id" and report_prefix is not null and org_unit_id = orgUnitId
and batch &lt ;NEW.batch group by batch
LOOP
UPDATE "public"."eod_report" SET delayed_points =
(delayed_points + eodRow.ptSum),former_delayed_sales =
END LOOP;
END IF;
RETURN NULL;
And I am getting this error when I try to execute the query:
ERROR:
syntax error at or near ";"
LINE 453: ..._type = 4 and org_unit_id = orgUnitId and id &lt ;NEW."id";
^
SQL state: 42601
Character: 15018
there is a screenshot where the error is highlighted
Looks like the < and > chars are replaced with &lt ; so the syntax is incorrect.
fix the SQL by replacing all &lt ; with < to have
... org_unit_id = orgUnitId and id < NEW."id";
Also &gt ; must be replaced with >
It could happens e.g. because of URL encoding of text sent from UI (browser)
You find the same problem in multiple parts of your query, because HTML entities like "&lt ;" and "&gt ;" are not legal in an SQL query, which the error points to. You should change them to "<" and ">" accordingly.

Merge into gives error ORA-30926: unable to get a stable set of rows in the source tables

I try to run the next 2 queries sequentially. The first one runs perfectly, the second one throws
ORA-30926: unable to get a stable set of rows in the source tables
I searched the net for a solution but I can't replicate it for my queries. Can anyone help me please?
Query 1:
merge into sdc_compare_person dcip
using (
select anumber, position, character
from sdc_diakrietposities_cip
where kind = 'Surname'
) x
on (dcip.sourcekey = x.anumber)
when matched then update set
dcip.GESVOR = substr(dcip.GESVOR, 1, x.position - 1) ||
x.character ||
substr(dcip.GESVOR, x.position + 1, length(dcip.GESVOR)-x.position)
;
188 rows merged.
Query 2:
merge into sdc_compare_person dcip
using (
select anumber, position, character
from sdc_diakrietposities_cip
where kind = 'Lastname'
) x
on (dcip.sourcekey = x.anumber)
when matched then update set
dcip.GESNAM_D = substr(dcip.GESNAM_D, 1, x.position - 1) ||
x.character ||
substr(dcip.GESNAM_D, x.position + 1, length(dcip.GESNAM_D) - x.position)
;
SQL Error: ORA-30926: Unable to get a stable set of rows in the source tables
You can alway use ordinary update, it's not so elegant as MERGE, but should work:
UPDATE sdc_compare_person dcip
SET dcip.GESNAM_D = (
SELECT substr(dcip.GESNAM_D, 1, x.position - 1) ||
x.character ||
substr(dcip.GESNAM_D, x.position + 1, length(dcip.GESNAM_D) -
x.position)
FROM sdc_diakrietposities_cip x
where kind = 'Lastname'
AND dcip.sourcekey = x.anumber
)
WHERE dcip.sourcekey IN (
select anumber
from sdc_diakrietposities_cip
where kind = 'Lastname'
);
From the comments to the question it becomes clear that the author wants to update the same record many times.
Of course, this cannot get past ORA-30926 when trying to do it by a merge construct.
It's hard or impossible to do such a thing in pure oracle sql, but it's easily done with a pl/sql function.
For example:
create or replace function replace_chars(p_str varchar2, p_id number, p_kind varchar2) return varchar2 as
l_str varchar2(32767):=p_str;
begin
for u in (select u.position, u.character from sdc_diakrietposities_cip u
where u.anumber=p_id and u.kind=p_kind order by u.position) loop
if (u.position >= 1 or u.position <= length(l_str)) then
l_str:=substr(l_str, 1, u.position-1)|| u.character || substr(l_str, u.position+1);
end if;
end loop;
return l_str;
end;
Use like this:
update sdc_compare_person t
set t.GESNAM_D= replace_chars(t.GESNAM_D, t.sourcekey, 'Lastname');
I'd suggest backing up your table before running this.