Postgres rule to substitute word in WHERE clause - postgresql

Is it possible to replace a word within a WHERE clause of a SELECT statement by a rule (CREATE RULE myrule AS ON SELECT TO mytable...)?
SELECT col1, col2
FROM mytable
WHERE col2 = 1;
Should become
SELECT col1, col2
FROM mytable
WHERE col3 = 1;
The initial statement is issued by a client which does not allow the substitution. Hence the substitution has to be done on the database side.
** edit **
I might not have been specific enough.
I need a column name substitution for this kind of pattern of the select statement. The WHERE clause constantly changes. As well as the
Is it possible to refer to the current SELECT statement in the rule itself?
Broken down to the bare minimum this is what I need:
take the current SELECT statement (if it has this certain pattern)
replace "WHERE col2" with "WHERE col3"
packed into a rule which can be deployed on the database side of things

Use case when statement
SELECT col1, col2, col3
FROM mytable
WHERE CASE WHEN foo='bar' THEN col3 ELSE col1 END = 1;

Related

Postgresql conditionally include distinct in query

Postgresql conditionally include distinct in query
Is there way to modify a query such as:
select distinct col1, col2
from our_schema.our_table
where (id = '1001')
The goal is to easily activate/deactivate the distinct keyword.
Obviously, one could move it to a comment such as:
select col1, col2 -- distinct
from our_schema.our_table
where (id = '1001')
Is there any easy way to do this in Postgresql?
I've seen 'dynamic SQL' in Microsoft SSMS using TSQL language.
Is there something like this for Postgresql? Or something even simpler?
Seems like this is just about code management / building SQL strings?
Insert a line break after DISTINCT. The only significance of white space in SQL is to separate tokens. Other than that, line breaks are purely cosmetic - except for standard comments starting with -- which end with the line.
SELECT DISTINCT
col1, col2 ...
-->
SELECT -- DISTINCT
col1, col2 ...
Or even:
SELECT
DISTINCT
col1, col2 ...
-->
SELECT
-- DISTINCT
col1, col2 ...
Or use C-style block comments: /* comment */
SELECT DISTINCT col1, col2 ...
-->
SELECT /*DISTINCT*/ col1, col2 ...

How to describe columns (get their names, data types, etc.) of a SQL query in PostgreSQL

I need a way to get a "description" of the columns from a SELECT query (cursor), such as their names, data types, precision, scale, etc., in PostgreSQL (or better yet PL/pgSQL).
I'm transitioning from Oracle PL/SQL, where I can get such description using a built-in procedure dbms_sql.describe_columns. It returns an array of records, one for each column of a given (parsed) cursor.
EDB has it implemented too (https://www.enterprisedb.com/docs/en/9.0/oracompat/Postgres_Plus_Advanced_Server_Oracle_Compatibility_Guide-127.htm#P13324_681237)
An examples of such query:
select col1 from tab where col2 = :a
I need an API (or a workaround) that could be called like this (hopefully):
select query_column_description('select col1 from tab where col2 = :a');
that will return something similar to:
{{"col1","numeric"}}
Why? We build views where these queries become individual columns. For example, view's query would look like the following:
select (select col1 from tab where col2 = t.colA) as col1::numeric
from tab_main t
http://sqlfiddle.com/#!17/21c7a/2
You can use systems table :
First step create a temporary view with your query (without clause where)
create or replace view temporary view a_view as
select col1 from tab
then select
select
row_to_json(t.*)
from (
select
column_name,
data_type
from
information_schema.columns
where
table_schema = 'public' and
table_name = 'a_view'
) as t

select all except for a specific column

I have a table with more than 20 columns, I want to get all columns except for one which I'll use in a conditional expression.
SELECT s.* (BUT NOT column1),
CASE WHEN column1 is null THEN 1 ELSE 2 END AS column1
from tb_sample s;
Can I achieve it in postgresql given the logic above?
It may not be ideal, but you can use information_schema to get the columns and use the column to exclude in the where clause.
That gives you a list of all the column names you DO want, which you can copy/paste into your select query:
select textcat(column_name, ',')
from information_schema.columns
where table_name ='table_name' and column_name !='column_to_exclude';

Does PostgreSQL have an equivalent to SAS' obsnum?

When converting scripts, tables, datasets, etc. from a SAS environment to a PostgreSQL environment, is there an equivalent to referencing SAS' obsnum in PostgreSQL? For example, if a query says:
SELECT FROM schema.table
WHERE obsnum = 1
Is there a way to track the observation number or similar in PostgreSQL? Or should a different approach be taken?
Thanks.
Should probably specify that I was told obsnum is a built-in SAS value associated with datasets and tables, and in my SAS scripts there is no declaration for obsnum, only a singular reference to it in a SELECT statement.
OBSNUM isn't an automatic variable in SAS so its a variable value.
You should be able to use a similar query in Postgres to limit where the variable value is 1.
To add on - PROC SQL doesn't have an automatic variable to do numbering, it can use monotonic() for row numbers but it's not supported.
(wrong answer):
Try CTID
See your previous question, the first answer and the comments/questions.
What exactly is the/this data statement in SAS doing? PostgreSQL equivalent?
PostgresSQL
http://www.postgresql.org/docs/8.2/static/ddl-system-columns.html
-- make(fake) a dataset
CREATE TABLE dataset
( val double precision NOT NULL
);
-- populate it with random
INSERT INTO dataset(val)
SELECT random()
FROM generate_series(1,100)
;
-- Use row_number() to enumerate the unordered tuples
-- note the subquery. It is needed because otherwise
-- you cannot refer to row_number
SELECT * FROM (
SELECT val
, row_number() OVER() AS obsnum
FROM dataset
) qq -- subquery MUST have an alias
WHERE qq.obsnum = 1
;
-- you can just as well order over ctid (the result is the same)
SELECT * FROM (
SELECT val
, row_number() OVER(ORDER BY ctid) AS obsnum
FROM dataset
) qq -- subquery MUST have an alias
WHERE qq.obsnum = 1
;
-- In stead of a subquery you could use
-- a CTE to wrap the enumeration part
WITH zzz AS (
SELECT val
, row_number() OVER(ORDER BY ctid) AS obsnum
FROM dataset
)
SELECT * FROM zzz
WHERE obsnum = 1
;
-- Or, if you just want one observation: use LIMIT
-- (order of records is not defined,
-- but the order of ctid is not stable either)
SELECT * FROM dataset
LIMIT 1
;

SELECT * except nth column

Is it possible to SELECT * but without n-th column, for example 2nd?
I have some view that have 4 and 5 columns (each has different column names, except for the 2nd column), but I do not want to show the second column.
SELECT * -- how to prevent 2nd column to be selected?
FROM view4
WHERE col2 = 'foo';
SELECT * -- how to prevent 2nd column to be selected?
FROM view5
WHERE col2 = 'foo';
without having to list all the columns (since they all have different column name).
The real answer is that you just can not practically (See LINK). This has been a requested feature for decades and the developers refuse to implement it. The best practice is to mention the column names instead of *. Using * in itself a source of performance penalties though.
However, in case you really need to use it, you might need to select the columns directly from the schema -> check LINK. Or as the below example using two PostgreSQL built-in functions: ARRAY and ARRAY_TO_STRING. The first one transforms a query result into an array, and the second one concatenates array components into a string. List components separator can be specified with the second parameter of the ARRAY_TO_STRING function;
SELECT 'SELECT ' ||
ARRAY_TO_STRING(ARRAY(SELECT COLUMN_NAME::VARCHAR(50)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME='view4' AND
COLUMN_NAME NOT IN ('col2')
ORDER BY ORDINAL_POSITION
), ', ') || ' FROM view4';
where strings are concatenated with the standard operator ||. The COLUMN_NAME data type is information_schema.sql_identifier. This data type requires explicit conversion to CHAR/VARCHAR data type.
But that is not recommended as well, What if you add more columns in the long run but they are not necessarily required for that query?
You would start pulling more column than you need.
What if the select is part of an insert as in
Insert into tableA (col1, col2, col3.. coln) Select everything but 2 columns FROM tableB
The column match will be wrong and your insert will fail.
It's possible but I still recommend writing every needed column for every select written even if nearly every column is required.
Conclusion:
Since you are already using a VIEW, the simplest and most reliable way is to alter you view and mention the column names, excluding your 2nd column..
-- my table with 2 rows and 4 columns
DROP TABLE IF EXISTS t_target_table;
CREATE TEMP TABLE t_target_table as
SELECT 1 as id, 1 as v1 ,2 as v2,3 as v3,4 as v4
UNION ALL
SELECT 2 as id, 5 as v1 ,-6 as v2,7 as v3,8 as v4
;
-- my computation and stuff that i have to messure, any logic could be done here !
DROP TABLE IF EXISTS t_processing;
CREATE TEMP TABLE t_processing as
SELECT *, md5(t_target_table::text) as row_hash, case when v2 < 0 THEN true else false end as has_negative_value_in_v2
FROM t_target_table
;
-- now we want to insert that stuff into the t_target_table
-- this is standard
-- INSERT INTO t_target_table (id, v1, v2, v3, v4) SELECT id, v1, v2, v3, v4 FROM t_processing;
-- this is andvanced ;-)
INSERT INTO t_target_table
-- the following row select only the columns that are pressent in the target table, and ignore the others.
SELECT r.* FROM (SELECT to_jsonb(t_processing) as d FROM t_processing) t JOIN LATERAL jsonb_populate_record(NULL::t_target_table, d) as r ON TRUE
;
-- WARNING : you need a object that represent the target structure, an exclusion of a single column is not possible
For columns col1, col2, col3 and col4 you will need to request
SELECT col1, col3, col4 FROM...
to omit the second column. Requesting
SELECT *
will get you all the columns