How to create trigger in phpmyadmin - triggers

I am having this error when I try to create a trigger in phpmyadmin. What am I doing wrong?
SQL query:
CREATE TRIGGER ins_social
AFTER INSERT ON sa_users_social
FOR EACH ROW
BEGIN
INSERT INTO fsb2_users ( u_social_id, u_auth, u_nickname, u_email, u_avatar, u_signature, u_language, u_joined, u_sexe, u_rank_id )
VALUES ('NEW.social_id', '1', 'username', 'email', 'photoURL', 'description', 'fr', 'CURRENT_TIMESTAMP', 'gender', '0');
Error:
#1064 - 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 '' at line 5

Doesn't like stored procedure, you don't have to put BEGIN...END there. Could you delete BEGIN and try again?
Or, if you need to put BEGIN there, you can do it like this:
delimiter #
CREATE TRIGGER ins_social
AFTER INSERT ON sa_users_social
FOR EACH ROW
BEGIN
INSERT INTO fsb2_users ( u_social_id, u_auth, u_nickname, u_email, u_avatar, u_signature, u_language, u_joined, u_sexe, u_rank_id )
VALUES ('NEW.social_id', '1', 'username', 'email', 'photoURL', 'description', 'fr', 'CURRENT_TIMESTAMP', 'gender', '0');
END#
delimiter ;
Another possible error is that you put single quote around each value, which makes them passed as varchars. In the statement, I guess u_social_id is an numerical type, right? So you pass a varchar value to a numerical type field, that's a problem. Since you didn't post your table structure here. It's just a guess. :)

Related

Insert values in temporary table from xml using Xmltable postgres

I am trying to pass xml nodes as a parameter and extract values from them and insert them into a table. I am able to do so if I pass the entire xml node but if I try to pass it through a variable I am facing syntax error. Since I will be having dynamic xml which will need to be parsed , I have to pass it via variable only. I am providing a simplified version below of what I am trying to achieve. I am getting a syntax error 'syntax error at or near "xmlvalue"'
CREATE TEMPORARY TABLE SAMPLE(Id text,Author text,Title text);
xmlvalue text := '<Book><Id>1</Id><Author>Subhrendu</Author><Title>Postgre</Title></Book>';
with data as (
select xmlvalue::xml val)
--select '<Book><Id>1</Id><Author>Subhrendu</Author><Title>Postgre</Title></Book>'::xml val)
INSERT INTO SAMPLE(Id, Author,Title)
SELECT Id, Author,Title
FROM data x,
XMLTABLE('/Book'
PASSING val
COLUMNS
Id text PATH 'Id',
Author text PATH 'Author',
Title text PATH 'Title' )
;
select * from sample
Edit 1 : As suggested I am now trying to wrap the above code inside a function, since we can't use variables outside procedures/functions.
create or replace function xml()
returns table(Id text,Author Text,Title Text)
as $$
declare
xmlvalue text := '<Book><Id>1</Id><Author>Subhrendu</Author><Title>Postgre</Title></Book>';
begin
CREATE TEMPORARY TABLE SAMPLE(Id text,Author text,Title text);
with data as (
select xmlvalue::xml val)
INSERT INTO SAMPLE(Id, Author,Title)
SELECT Id, Author,Title
FROM data x,
XMLTABLE('/Book'
PASSING val
COLUMNS
Id text PATH 'Id',
Author text PATH 'Author',
Title text PATH 'Title' )
;
return query
select s.Id,s.Author,s.Title from sample s ;
end;
$$
language plpgsql
While trying to execute the above function I am getting the below errors. What I understand from the error is I have to provide the table alias name to refer to a column.
ERROR: column reference "id" is ambiguous
LINE 4: SELECT Id, Author,Title
^
DETAIL: It could refer to either a PL/pgSQL variable or a table column.
QUERY: with data as (
select xmlvalue::xml val)
INSERT INTO SAMPLE(Id, Author,Title)
SELECT Id, Author,Title
FROM data x,
XMLTABLE('/Book'
PASSING val
COLUMNS
Id text PATH 'Id',
Author text PATH 'Author',
Title text PATH 'Title' )
CONTEXT: PL/pgSQL function xml() line 6 at SQL statement
SQL state: 42702
This works.
create or replace function xml()
returns table(Id text,Author Text,Title Text)
as $$
declare
xmlvalue text := '<Book><Id>1</Id><Author>Subhrendu</Author><Title>Postgre</Title></Book>';
begin
CREATE TEMPORARY TABLE SAMPLE(Id text,Author text,Title text);
with data as (
select xmlvalue::xml val)
INSERT INTO SAMPLE(Id, Author,Title)
SELECT d.Id,d.Author,d.Title
FROM data x,
XMLTABLE('/Book'
PASSING val
COLUMNS
Id text PATH 'Id',
Author text PATH 'Author',
Title text PATH 'Title' ) as d
;
return query
select s.Id,s.Author,s.Title from sample s ;
end;
$$
language plpgsql

allow variadic unknowns in function arguments

I want to create a small helper function that will allow me to stop repeating this code 100s of times:
SELECT
jsonb_strip_nulls(
jsonb_build_object(
'hello', 'world',
'value', 5,
'error', null
)
);
-- returns {"hello": "world","value":5}
However, when I try to wrap this in a function I get errors because the string literals are technically unknowns:
CREATE OR REPLACE FUNCTION jsonb_build_nullless_object(VARIADIC anyarray) RETURNS jsonb AS $$
SELECT
jsonb_strip_nulls(
jsonb_build_object(
VARIADIC $1
)
)
$$ LANGUAGE sql IMMUTABLE;
SELECT jsonb_build_nullless_object(
'hello', 'world',
'value', 5,
'error', null
);
-- ERROR: invalid input syntax for integer: "hello" has type unknown
Since the plain jsonb_build_object handles un-explicitly-typed string literals just fine, I assume there is a function decorator that would allow me to do the same?
There is no reason to use the pseudo-type anyarray as the arguments always are texts:
CREATE OR REPLACE FUNCTION jsonb_build_nullless_object(VARIADIC text[])
...
Db<>fiddle.
Note that arguments on odd positions are texts, so the whole array of arguments has to be text[]. SQL functions cannot have arguments of type "any" in contrast to some built-in functions like jsonb_build_object(VARIADIC "any").

How to execute a string result of a stored procedure in postgres

I have created the following stored procedure, which basically receives a name of table, and a prefix. The function then finds all columns that share this prefix and returns as an output a 'select' query command ('myoneliner').
as follows:
CREATE OR REPLACE FUNCTION mytext (mytable text, myprefix text)
RETURNS text AS $myoneliner$
declare
myoneliner text;
BEGIN
SELECT 'SELECT ' || substr(cols,2,length(cols)-2) ||' FROM '||mytable
INTO myoneliner
FROM (
SELECT array(
SELECT DISTINCT quote_ident(column_name::text)
FROM information_schema.columns
WHERE table_name = mytable
AND column_name LIKE myprefix||'%'
order by quote_ident
)::text cols
) sub;
RETURN myoneliner;
END;
$myoneliner$ LANGUAGE plpgsql;
Call:
select mytext('dkj_p_k27ac','enri');
As a result of running this stored procedure and the 'select' that is following it, I get the following output at the Data Output window (all within one cell, named "mytext text"):
'SELECT enrich_d_dkj_p_k27ac,enrich_lr_dkj_p_k27ac,enrich_r_dkj_p_k27ac
FROM dkj_p_k27ac'
I would like to basically be able to take the output command line that I received as an output and execute it. In other words, I would like to be able and execute the output of my stored procedure.
How can I do so?
I tried the following:
CREATE OR REPLACE FUNCTION mytext (mytable text, myprefix text)
RETURNS SETOF RECORD AS $$
declare
smalltext text;
myoneliner text;
BEGIN
SELECT 'SELECT ' || substr(cols,2,length(cols)-2) ||' FROM '||mytable
INTO myoneliner
FROM (
SELECT array(
SELECT DISTINCT quote_ident(column_name::text)
FROM information_schema.columns
WHERE table_name = mytable
AND column_name LIKE myprefix||'%'
order by quote_ident
)::text cols
) sub;
smalltext=lower(myoneliner);
raise notice '%','my additional text '||smalltext;
RETURN QUERY EXECUTE smalltext;
END;
$$ LANGUAGE plpgsql;
Call function:
SELECT * from mytext('dkj_p_k27ac','enri');
But I'm getting the following error message, could you please advise what should I change in order for it to execute?:
ERROR: a column definition list is required for functions returning "record"
LINE 26: SELECT * from mytext('dkj_p_k27ac','enri');
********** Error **********
ERROR: a column definition list is required for functions returning "record"
SQL state: 42601
Character: 728
Your first problem was solved by using dynamic SQL with EXECUTE like Craig advised.
But the rabbit hole goes deeper:
CREATE OR REPLACE FUNCTION myresult(mytable text, myprefix text)
RETURNS SETOF RECORD AS
$func$
DECLARE
smalltext text;
myoneliner text;
BEGIN
SELECT INTO myoneliner
'SELECT '
|| string_agg(quote_ident(column_name::text), ',' ORDER BY column_name)
|| ' FROM ' || quote_ident(mytable)
FROM information_schema.columns
WHERE table_name = mytable
AND column_name LIKE myprefix||'%'
AND table_schema = 'public'; -- schema name; might be another param
smalltext := lower(myoneliner); -- nonsense
RAISE NOTICE 'My additional text: %', myoneliner;
RETURN QUERY EXECUTE myoneliner;
END
$func$ LANGUAGE plpgsql;
Major points
Don't cast the whole statement to lower case. Column names might be double-quoted with upper case letters, which are case-sensitive in this case (no pun intended).
You don't need DISTINCT in the query on information_schema.columns. Column names are unique per table.
You do need to specify the schema, though (or use another way to single out one schema), or you might be mixing column names from multiple tables of the same name in multiple schemas, resulting in nonsense.
You must sanitize all identifiers in dynamic code - including table names: quote_ident(mytable). Be aware that your text parameter to the function is case sensitive! The query on information_schema.columns requires that, too.
I untangled your whole construct to build the list of column names with string_agg() instead of the array constructor. Related answer:
Update multiple columns that start with a specific string
The assignment operator in plpgsql is :=.
Simplified syntax of RAISE NOTICE.
Core problem impossible to solve
All of this still doesn't solve your main problem: SQL demands a definition of the columns to be returned. You can circumvent this by returning anonymous records like you tried. But that's just postponing the inevitable. Now you have to provide a column definition list at call time, just like your error message tells you. But you just don't know which columns are going to be returned. Catch 22.
Your call would work like this:
SELECT *
FROM myresult('dkj_p_k27ac','enri') AS f (
enrich_d_dkj_p_k27ac text -- replace with actual column types
, enrich_lr_dkj_p_k27ac text
, enrich_r_dkj_p_k27ac text);
But you don't know number, names (optional) and data types of returned columns, not at creation time of the function and not even at call time. It's impossible to do exactly that in a single call. You need two separate queries to the database.
You could return all columns of any given table dynamically with a function using polymorphic types, because there is a well defined type for the whole table. Last chapter of this related answer:
Refactor a PL/pgSQL function to return the output of various SELECT queries

Select statement after insert hangs?

In my stored procedure I am trying to insert records in to a temporary table
In my stored procedure I am trying to insert records in to a temporary table
--Temporary table
create table #CR_TMP
(
ct_co_id int NULL
, ct_nbr int NULL
, ctrct_srvc_type char(4) NULL
, start_date datetime NULL
, end_date datetime NULL
)
print 'Before insert'
Insert into #CR_TMP
select distinct col1,col2,......
from tableName
where conditions
print 'After insert'
select '#CR_TMP' as #CR_TMP, * from #CR_TMP
print 'here 1'
I ran the select query and it gives about 583 rows.
But when I execute the above procedure. I think it's getting stuck on the insert procedure.
I do get the result 'After insert' but I don't get the results for print 'here 1'.
I had this procedure executing for 2 hours and it was stuck at the same place. Any pointers here?
I ran the select query and it gives about 583 rows.
But when I execute the above procedure. I think it's getting stuck on the insert procedure.
I do get the result 'After insert' but I don't get the results for print 'here 1'.
I had this procedure executing for 2 hours and it was stuck at the same place. Any pointers here?
The procedure looks good except for this part:
print 'After insert'
select '#CR_TMP' as #CR_TMP, *
from #CR_TMP
print 'here 1'
Try changing this to:
print 'After insert'
select '#CR_TMP' as [#CR_TMP], *
from #CR_TMP
print 'here 1'
Or even remove the first part of the select
print 'After insert'
select *
from #CR_TMP
print 'here 1'
Edit:
After a discussion in chat, it was determined that the initial portion of the stored procedure that sanika thought was the issue actually was working. So I advised that they start working query by query back into a test to determine where the actual problem is. At that point, it will be easier to debug a 30 page stored procedure.

Typecast string to integer

I am importing data from a table which has raw feeds in Varchar, I need to import a column in varchar into a string column. I tried using the <column_name>::integer as well as to_number(<column_name>,'9999999') but I am getting errors, as there are a few empty fields, I need to retrieve them as empty or null into the new table.
Wild guess: If your value is an empty string, you can use NULLIF to replace it for a NULL:
SELECT
NULLIF(your_value, '')::int
You can even go one further and restrict on this coalesced field such as, for example:-
SELECT CAST(coalesce(<column>, '0') AS integer) as new_field
from <table>
where CAST(coalesce(<column>, '0') AS integer) >= 10;
If you need to treat empty columns as NULLs, try this:
SELECT CAST(nullif(<column>, '') AS integer);
On the other hand, if you do have NULL values that you need to avoid, try:
SELECT CAST(coalesce(<column>, '0') AS integer);
I do agree, error message would help a lot.
The only way I succeed to not having an error because of NULL, or special characters or empty string is by doing this:
SELECT REGEXP_REPLACE(COALESCE(<column>::character varying, '0'), '[^0-9]*' ,'0')::integer FROM table
I'm not able to comment (too little reputation? I'm pretty new) on Lukas' post.
On my PG setup to_number(NULL) does not work, so my solution would be:
SELECT CASE WHEN column = NULL THEN NULL ELSE column :: Integer END
FROM table
If the value contains non-numeric characters, you can convert the value to an integer as follows:
SELECT CASE WHEN <column>~E'^\\d+$' THEN CAST (<column> AS INTEGER) ELSE 0 END FROM table;
The CASE operator checks the < column>, if it matches the integer pattern, it converts the rate into an integer, otherwise it returns 0
Common issue
Naively type casting any string into an integer like so
SELECT ''::integer
Often results to the famous error:
Query failed: ERROR: invalid input syntax for integer: ""
Problem
PostgreSQL has no pre-defined function for safely type casting any string into an integer.
Solution
Create a user-defined function inspired by PHP's intval() function.
CREATE FUNCTION intval(character varying) RETURNS integer AS $$
SELECT
CASE
WHEN length(btrim(regexp_replace($1, '[^0-9]', '','g')))>0 THEN btrim(regexp_replace($1, '[^0-9]', '','g'))::integer
ELSE 0
END AS intval;
$$
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
Usage
/* Example 1 */
SELECT intval('9000');
-- output: 9000
/* Example 2 */
SELECT intval('9gag');
-- output: 9
/* Example 3 */
SELECT intval('the quick brown fox jumps over the lazy dog');
-- output: 0
you can use this query
SUM(NULLIF(conversion_units, '')::numeric)
And if your column has decimal points
select NULLIF('105.0', '')::decimal
This works for me:
select (left(regexp_replace(coalesce('<column_name>', '0') || '', '[^0-9]', '', 'g'), 8) || '0')::integer
For easy view:
select (
left(
regexp_replace(
-- if null then '0', and convert to string for regexp
coalesce('<column_name>', '0') || '',
'[^0-9]',
'',
'g'
), -- remove everything except numbers
8 -- ensure ::integer doesn't overload
) || '0' -- ensure not empty string gets to ::integer
)::integer
The perfect solution for me is to use nullif and regexp_replace
SELECT NULLIF(REGEXP_REPLACE('98123162t3712t37', '[^0-9]', '', 'g'), '')::bigint;
Above solution consider the following edge cases.
String and Number: only the regexp_replace function perfectly converts into integers.
SELECT NULLIF(REGEXP_REPLACE('string and 12345', '[^0-9]', '', 'g'), '')::bigint;
Only string: regexp_replace converts non-string characters to empty strings; which can't cast directly to integer so use nullif to convert to null
SELECT NULLIF(REGEXP_REPLACE('only string', '[^0-9]', '', 'g'), '')::bigint;
Integer range: Converting a string into integer may cause out of range for type integer error. So use bigint instead
SELECT NULLIF(REGEXP_REPLACE('98123162t3712t37', '[^0-9]', '', 'g'), '')::bigint;