Please, understand that I am totally new to Postgresql. I am having trouble testing queries in client tools like pgAdmin and DBeaver.
This works:
select *
FROM pg_tables
WHERE schemaname = 'schema_name'
AND tablename = 'table_name'
But not this:
RAISE NOTICE 'Hi' ;
I also can't run anything with an "if" statement. I have years of experience with SSMS, and so I expect to be able to run statements like this. Am I using the tool wrong or are they just not capable of these types of statements? Thank you.
Thank you to #a_horse_with_no_name. I needed to be using a do block.
DO $$
BEGIN
RAISE NOTICE 'my message';
END; $$;
DO $$
begin
IF EXISTS (SELECT FROM pg_tables WHERE schemaname = 'schema_name' AND tablename = 'table_name') then
RAISE NOTICE 'exists' ;
ELSE
RAISE NOTICE 'not exists' ;
end IF;
END; $$;
Related
I want to give all privileges to each table in the database。
sql as follows:
CREATE OR REPLACE FUNCTION test()
RETURNS void
AS $$
DECLARE
tb RECORD;
sql1 TEXT;
BEGIN
FOR tb IN (select tablename from pg_tables where schemaname='public')
LOOP
sql1 := 'GRANT ALL ON table ' || tb.tablename || ' TO tmp_admin';
EXECUTE sql1;
--RAISE NOTICE '%s', sql1;
END LOOP;
END
$$ LANGUAGE PLPGSQL;
select * FROM test();
when i RAISE NOTICE, it can normal output the sql i wanted。but this sql always effect。
but this sql always effect
I don't know what this means. Presumably you mean it doesn't do the right thing, otherwise you wouldn't have a question to ask.
As the horse mentioned, you don't to complicate this with a function at all. But regardless, it does work for me, as long as your table names don't need to be double-quoted. You should use FORMAT() rather than || to construct your string, to avoid that problem.
If you get an error, what is the error you get? If you think it silently has no effect, what did you do to infer the lack of effect?
Could you please advise me SQL-based or database PostgreSQL specific command(s) or flow to create thousands or even millions of similar (same) 2-column-based empty tables in PostgreSQL RDBMS? Maybe based on basic empty table as token 'fw'? 'fw_'+(increment)
The solution has to be as fast as possible. Maybe some trick(?) Thank you.
You can use dynamic SQL in a loop.
do
$$
declare
l_counter integer;
l_name text;
l_sql text;
begin
for l_counter in 1..10000 loop
l_name := 'fw_'||to_char(l_counter, 'FM00000000');
l_sql := format('create table %I (col1 integer, col2 integer)', l_name);
execute l_sql;
end loop;
end;
$$;
But this sounds like a really bad idea.
Creating 10000 tables or orders on magnitude more is not going to be blindingly fast - it's just not going to happen. But the following MAY be a little quicker.
do $$
declare stmt record;
begin
for stmt in
with s as (select (generate_series (1,100)) n )
select 'create table tbl_' || to_char(n, 'FM00000000') || ' (i1 integer, i2 integer); ' as t from s
loop
execute stmt.t ;
end loop;
end ; $$;
As suggested let us know what issue your trying to resolve. Perhaps then someone will see a variable alternation. I would ask: After you create these tables how will you use them and how will you know which one to use?
I'm trying to write some SQL to copy data from all of my PostgreSQL tables in a given database based on what's in the information_schema. It should output data files to my local machine ready for import to another machine. Ultimately, I'm going to tweak this so that I dump only select portions of tables (some of the tables I'm dumping have millions of records and I only want a small subset of data for testing purposes).
Here's what I have so far...
--Copy all tables...
DO
$$
DECLARE
formatstring text;
rec record;
BEGIN
RAISE NOTICE 'Copying tables...';
formatstring = 'COPY (select * from %I) to ''C:\Media\Code\%s.csv'';';
FOR rec IN
select table_name from information_schema.tables where table_schema = 'public' order by table_name
LOOP
RAISE NOTICE 'Table: %', rec.table_name;
RAISE NOTICE format(formatstring, rec.table_name, rec.table_name);
EXECUTE format(formatstring, rec.table_name, rec.table_name);
END LOOP;
END;
$$
LANGUAGE plpgsql;
However, I am getting this exception...
ERROR: unrecognized exception condition "format"
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 12
********** Error **********
ERROR: unrecognized exception condition "format"
SQL state: 42704
Context: compilation of PL/pgSQL function "inline_code_block" near line 12
The escaping of the single quotes seems fine (already checked this question: Insert text with single quotes in PostgreSQL). Indeed I can do the following and it works, with text being inserted into the formatting:
select format('COPY (select * from %I) to ''C:\Media\Code\%s.csv'';', 'system_user', 'system_user');
Can anyone assist with this issue? I can easily write a script or code that will generate the copy commands for me, but it would be great to do it all within a simple bit of SQL.
The cause is a syntax error in your 3nd RAISE statement. There are several valid formats, but you cannot feed an expression to RAISE directly. It has to be a string literal - with the option of string interpolation.
While being at it, I would simplify a couple of other things, too:
DO
$do$
DECLARE
_formatstring text := $$COPY %1$I TO 'C:\Media\Code\%1$s.csv'$$;
_sql text;
_tbl text;
BEGIN
RAISE NOTICE 'Copying tables...';
FOR _tbl IN
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
ORDER BY table_name
LOOP
_sql := format(_formatstring, _tbl);
RAISE NOTICE 'Table: %', _tbl;
RAISE NOTICE '%', _sql; -- fixed!!
EXECUTE _sql;
END LOOP;
END
$do$ LANGUAGE plpgsql;
Major points
The plain table name with COPY instead of SELECT * FROM tbl.
The use of nested dollar-quotes.
Format specifiers %1$I and %1$s for the format() function, so we only need to supply the table name once.
You can assign variables at declaration time.
The scalar variable instead of a record in the FOR loop - we only need the one column anyway.
This question already has answers here:
Use text output from a function as new query
(2 answers)
Closed 8 years ago.
Using the following code I can select a few columns that share the same prefixes (either upreg_srt or downreg_srt) from my table and delete (drop) them:
DO
$do$
DECLARE
_column TEXT;
BEGIN
FOR _column IN
SELECT DISTINCT quote_ident(column_name)
FROM information_schema.columns
WHERE table_name = 'all_se_13patients_downreg_ranks'
AND column_name LIKE '%upreg_srt' OR column_name LIKE '%downreg_srt'
AND table_schema NOT LIKE 'pg_%'
order by quote_ident
LOOP
RAISE NOTICE '%',
-- EXECUTE
'ALTER TABLE all_se_13patients_downreg_ranks DROP COLUMN ' || _column;
END LOOP;
END
$do$
This code works nicely under Postgres. (Demark the --EXECUTE line first of course!)
Is there a way to utilize/alter this code (or to use different scripting) in order to actually save the chosen columns (the ones with shared prefixes) into a daughter table? Pseudo-code:
select [my chosen columns]
into myNewTbl
from myOriginalTbl
I was able to run the following code:
DO
$do$
DECLARE
qry TEXT;
BEGIN
SELECT 'SELECT id_13,' || substr(cols,2,length(cols)-2) ||
' FROM all_se_13patients_downreg_ranks' INTO qry
FROM (
SELECT array(
SELECT DISTINCT quote_ident(column_name::text)
FROM information_schema.columns
WHERE table_name = 'all_se_13patients_downreg_ranks'
AND column_name LIKE '%downreg_srt'
order by quote_ident
)::text cols
-- CAST text so we can just strip off {}s and have column list
) sub;
--EXECUTE qry;
RAISE NOTICE '%',qry;
END
$do$
It works nicely - but I can't use the EXECUTE qry line for some reason.
If I try the RAISE NOTICE '%',qry; line I get an output - which is basically the command line that I can later copy/paste and execute it just fine in a new query window(!). Therefore, I'm wondering why the EXECUTE part doesn't work?
Running the procedure with the RAISE NOTICE line I get:
NOTICE: SELECT
id_13,agk_downreg_srt,bvi_downreg_srt,cbk_downreg_srt,dj_downreg_srt,dkj_downreg_srt,flv_downreg_srt,ghw_downreg_srt,gvz_downreg_srt,idy_downreg_srt,prw_downreg_srt,spn_downreg_srt,zgr_downreg_srt,znk_downreg_srt
FROM all_se_13patients_downreg_ranks
However, if I try to run the procedure with the EXECUTE part instead I get:
Query returned successfully with no result in 51 ms.
So the problem is that postgres fails to actually execute the command line. The question is WHY? And is there a better way to perform this procedure so it actually executes?
However, if I try to run the procedure with the EXECUTE part instead I
get: "Query returned successfully with no result in 51 ms." - so the
problem is that postgres fails to actually execute the command line
No, PostgreSQL successfully executed the query. That's what "Query returned successfully" means. It returned no result, and it took 51 ms.
If you want to execute a dynamic SELECT statement, and you want to see some kind of result, use execute ... into.
do
$$
declare
qry text;
table_name text;
begin
qry := 'select table_name from information_schema.tables where table_name like ''pg_%'';';
raise notice '%', qry;
execute qry into table_name;
raise notice '%', table_name;
END
$$
NOTICE: select table_name from information_schema.tables where table_name like 'pg_%';
NOTICE: pg_statistic
Query returned successfully with no result in 24 ms.
The value "pg_statistic" was the first row in the result set. Using execute this way assigns the value of only the first row to table_name. This is by design.
If you want to insert the column names into a table, you need to write an INSERT statement, not a SELECT statement.
I would like to write "procedures" in DB2 9.7 without defining the "CREATE PROCEDURE" -statement. Apparently this is something called "compiled" sql statement. However, I am having problems in getting valid syntax. E.g. the syntax below does not seem to work:
BEGIN
DECLARE V_SQL VARCHAR(1024);
SET V_SQL = 'BEGIN
IF EXISTS(SELECT NAME FROM SYSIBM.SYSTRIGGERS WHERE NAME = ''TRIGGER_EMPLOYEE_FOR_DELETES'') THEN
DROP TRIGGER TRIGGER_EMPLOYEE_FOR_DELETES;
END IF;
END;';
PREPARE S1 FROM V_SQL;
EXECUTE S1;
END
I have tried adding/removing ";" and statement symbol "!" but still cannot get it to work.
You cannot have a DROP TRIGGER statement within a compound SQL statement. See the DB2 documentation for compound SQL.
If you are able to move the IF statement outside of V_SQL, you could do something like this:
BEGIN
DECLARE V_SQL VARCHAR(1024);
IF EXISTS(SELECT NAME FROM SYSIBM.SYSTRIGGERS
WHERE NAME = 'TRIGGER_EMPLOYEE_FOR_DELETES'
) THEN
SET V_SQL = 'DROP TRIGGER TRIGGER_EMPLOYEE_FOR_DELETES;';
PREPARE S1 FROM V_SQL;
EXECUTE S1;
END IF;
END
Of course, this wouldn't work if you need to set your condition dynamically.