I want to create two new columns from a query in postgresql, one depending on existing data, and the other depnding on the new column, i.e
existing_col new_col new_col2
a 1 2
b 0 0
I have tried:
select existing_col,
case when existing_col like 'a' then 1 else 0 end as new_col
case when new_col like 1 then 2 else 0 end as new_col2
from table
however this is giving me the error that new_col doesn't exist, how can I achieve this?
updated:
(I modified your qry a little no avoid like operator for integers)
t=# create table "table" (existing_col text);
CREATE TABLE
Time: 50.189 ms
t=# insert into "table" values('a'),('b');
INSERT 0 2
Time: 0.911 ms
t=# select *,case when new_col like 1 then 2 else 0 end as new_col2
t-# from (
t(# select existing_col,
t(# case when existing_col like 'a' then 1 else 0 end as new_col
t(# from "table") al
t-# ;
ERROR: operator does not exist: integer ~~ integer
LINE 1: select *,case when new_col like 1 then 2 else 0 end as new_c...
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
Time: 0.514 ms
t=# select *,case when new_col = 1 then 2 else 0 end as new_col2
t-# from (
t(# select existing_col,
t(# case when existing_col like 'a' then 1 else 0 end as new_col
t(# from "table") al
t-# ;
existing_col | new_col | new_col2
--------------+---------+----------
a | 1 | 2
b | 0 | 0
(2 rows)
Time: 0.347 ms
as in docs:
CASE WHEN condition THEN result
[WHEN ...]
[ELSE result]
END
Related
For the sake of example, there's five columns in a table month:
month.week1
month.week2
month.week3
month.week4
month.week5
The number of col is determined by a function
EXTRACT(WEEK FROM NOW()) - EXTRACT(WEEK FROM DATE_TRUNC('month', NOW())) + 1
How can I select the column colX? This is what I have so far
SELECT month.week || (
EXTRACT(WEEK FROM NOW())
- EXTRACT(WEEK FROM DATE_TRUNC('month', NOW())) + 1
)::text
FROM month
But that gives me the error
ERROR: column month.week doesn't exist
SQL state: 42703
Use case statement
SELECT
CASE WHEN (week expresion) = 1 THEN month.week1
WHEN (week expresion) = 2 THEN month.week2
WHEN (week expresion) = 3 THEN month.week3
WHEN (week expresion) = 4 THEN month.week4
ELSE month.week5
END as WeekValue
FROM month
OR
SELECT
CASE (week expresion)
WHEN 1 THEN month.week1
WHEN 2 THEN month.week2
WHEN 3 THEN month.week3
WHEN 4 THEN month.week4
ELSE month.week5
END as WeekValue
FROM month
dynamic sql sample:
t=# create table so58(i int,w1 text);
CREATE TABLE
t=# create or replace function so59(_n int) returns table (i int,w text) as $$begin
return query execute format('select i,w%s from so58',_n);
end;
$$ language plpgsql;
CREATE FUNCTION
t=# select * from so59(1);
i | w
---+---
(0 rows)
I have a journal system that has a table structure like:
[journal_logs]
id
title
created_at
[journal_entries]
id
journal_log_id
body
created_at
So a "journal_log" is made up of one or more "entries".
On my webpage I want to list the most recent logs, along with the entries.
Example:
"Log 10"
"entry 1"
"entry 2"
"entry 3"
"Log 9"
"entry 1"
"Log 8"
"entry 1"
"entry 2"
Is it possible to produce an output like this with a single query?
here is example with <UL><LI>
data sample:
t=# create table journal_logs(id int,title text);
CREATE TABLE
t=# create table journal_entries(id int,journal_log_id int);
CREATE TABLE
t=# insert into journal_logs select 8,'a';
INSERT 0 1
t=# insert into journal_logs select 9,'a';
INSERT 0 1
t=# insert into journal_logs select 10,'a';
INSERT 0 1
t=# insert into journal_entries select 1,8;
INSERT 0 1
t=# insert into journal_entries select 2,8;
INSERT 0 1
t=# insert into journal_entries select 1,9;
INSERT 0 1
t=# insert into journal_entries select 1,10;
INSERT 0 1
t=# insert into journal_entries select 2,10;
INSERT 0 1
t=# insert into journal_entries select 3,10;
INSERT 0 1
query sample:
t=# with p as (
select concat('<ul>',l.id,l.title) ul,concat('<li>',e.id) li
from journal_logs l
join journal_entries e on l.id = journal_log_id
)
select
concat(
case when li = min(li) over (partition by ul order by li) then ul end
,concat(li, case when li = max(li) over (partition by ul) then '</ul>' end)) html
from p;
html
------------------
<ul>10a<li>1
<li>2
<li>3</ul>
<ul>8a<li>1
<li>2</ul>
<ul>9a<li>1</ul>
(6 rows)
<ul>10a<li>1
<li>2
<li>3</ul>
<ul>8a<li>1
<li>2</ul>
<ul>9a<li>1</ul>
I'm probably overthinking this, because I'm not really sure where to start... But here goes:
I have the following
Tables
students
assessments
students_assessments
Expected output
First query for "Attempts"
student_id | Assessment 1 | Assessment 2 | Assessment 3 | Assessment 4
1 3 1 2 0
2 1 0 0 0
3 2 1 1 0
4 5 3 3 0
5 1 5 0 0
6 2 1 2 0
Second query for "passed"
student_id | Assessment 1 | Assessment 2 | Assessment 3 | Assessment 4
1 t t f f
2 t t f f
3 t t f f
4 t t t f
5 t t f f
6 t t t f
The part that is tripping me up is not doing a join for EVERY assessment, and not even defining the columns manually but rather "generating" each column for assessments that exist.
I feel like it's simple, and I am just too overworked to figure it out right now :) thank you in advance for your help, and here's a SQL Fiddle of data as an example
Simple query for "Attempts",
select student_id,sum(case when assessment_id=1 then 1 else 0 end) as "Assessment 1",
sum(case when assessment_id=2 then 1 else 0 end) as "Assessment 2",
sum(case when assessment_id=3 then 1 else 0 end) as "Assessment 3",
sum(case when assessment_id=4 then 1 else 0 end) as "Assessment 4",
sum(case when assessment_id=5 then 1 else 0 end) as "Assessment 5",
sum(case when assessment_id=6 then 1 else 0 end) as "Assessment 6"
from assessments_students
group by student_id
order by student_id
In crosstab() function also, need to define columns name explicitly like "Assessment 1","Assessment 2" and so on.
Or write custom function for creating dynamic query, and execute using EXECUTE statement.
DROP FUNCTION get_Attempts() ;
CREATE OR REPLACE FUNCTION get_Attempts() RETURNS text AS
$BODY$
DECLARE
r1 record;
str_query text := '';
BEGIN
str_query :='select student_id,';
FOR r1 IN SELECT "_id" , "name" FROM Assessments
LOOP
str_query:= str_query ||
'sum(case when assessment_id=' || r1."_id" || ' then 1 else 0 end) as "' || r1.name ||'",' ;
END LOOP;
str_query:=trim( trailing ',' from str_query); -- remove last semicolon
str_query:= str_query || ' from assessments_students group by student_id order by student_id';
return str_query;
END
$BODY$
LANGUAGE 'plpgsql' ;
SELECT * FROM get_Attempts();
Second query for "passed"
select student_id,
max(case when assessment_id=1 and passed='t' then 't' else 'f' end) as "Assessment 1",
max(case when assessment_id=2 and passed='t' then 't' else 'f' end) as "Assessment 2",
max(case when assessment_id=3 and passed='t' then 't' else 'f' end) as "Assessment 3",
max(case when assessment_id=4 and passed='t' then 't' else 'f' end) as "Assessment 4",
max(case when assessment_id=5 and passed='t' then 't' else 'f' end) as "Assessment 5",
max(case when assessment_id=6 and passed='t' then 't' else 'f' end) as "Assessment 6"
from assessments_students
group by student_id
order by student_id
and it's function look likes,
DROP FUNCTION get_passed() ;
CREATE OR REPLACE FUNCTION get_passed() RETURNS text AS
$BODY$
DECLARE
r1 record;
str_query text := '';
BEGIN
str_query :='select student_id,';
FOR r1 IN SELECT "_id" , "name" FROM Assessments
LOOP
str_query:= str_query ||
'max(case when assessment_id=' || r1."_id" || ' and passed=''t'' then ''t'' else ''f'' end) as "' || r1.name ||'",' ;
END LOOP;
str_query:=trim( trailing ',' from str_query); -- remove last semicolon
str_query:= str_query || ' from assessments_students group by student_id order by student_id';
return str_query;
END
$BODY$
LANGUAGE 'plpgsql' ;
SELECT * FROM get_passed();
SELECT * FROM crosstab(
'Select studentid, assessmentname,count (studentassessmentid) from ....[do ur joins here] group by studentid,assessmentname order by 1,2' AS ct (studentid, assesment1, assessment2,assesent3,assessment4);
I realized it doesn't work anymore. I'm using PostgreSQL 12.
So here you cannot return non-predefined table type (I mean variable columns) either select from varying char.
An example with using anonymous code block, crosstab and temp tables.
Cursors are redundand, I was solving a task that meant of using them.
CREATE EXTENSION IF NOT EXISTS tablefunc;
DO
$$
DECLARE
movie_probe CURSOR FOR
SELECT m.name movie_name, count(c.id) cinema_count
FROM movies m
JOIN sessions s ON m.id = s.movie_id
JOIN cinema_halls ch ON s.cinema_hall_id = ch.id
JOIN cinemas c ON ch.cinema_id = c.id
GROUP BY m.name
HAVING count(c.name) > 1
ORDER BY count(c.name) DESC;
movie_rec RECORD;
movie_columns TEXT DEFAULT '';
cinemas_probe CURSOR (cond_movie_name TEXT) FOR
SELECT m.name movie_name, c.name cinema_name
FROM movies m
JOIN sessions s ON m.id = s.movie_id
JOIN cinema_halls ch ON s.cinema_hall_id = ch.id
JOIN cinemas c ON ch.cinema_id = c.id
WHERE cond_movie_name = m.name
ORDER BY c.name;
cinema_rec RECORD;
cinema_row_counter INT DEFAULT 0;
BEGIN
DROP TABLE IF EXISTS cinema_multiples_aev;
CREATE TEMP TABLE cinema_multiples_aev (
row_id INT,
movie_name TEXT,
cinema_name TEXT
);
OPEN movie_probe;
LOOP
FETCH movie_probe INTO movie_rec;
EXIT WHEN NOT FOUND;
OPEN cinemas_probe(movie_rec.movie_name);
LOOP
FETCH cinemas_probe INTO cinema_rec;
EXIT WHEN NOT FOUND;
cinema_row_counter := cinema_row_counter + 1;
INSERT INTO cinema_multiples_aev (row_id, movie_name, cinema_name)
VALUES (cinema_row_counter, cinema_rec.movie_name, cinema_rec.cinema_name);
END LOOP;
CLOSE cinemas_probe;
cinema_row_counter := 0;
movie_columns := movie_columns || ', "' || movie_rec.movie_name || '" TEXT';
END LOOP;
CLOSE movie_probe;
movie_columns := substring(movie_columns FROM 2);
DROP TABLE IF EXISTS movie_multiples;
EXECUTE format('CREATE TEMP TABLE movie_multiples(row_id INT, %s)', movie_columns);
EXECUTE format(E'
INSERT INTO movie_multiples
SELECT *
FROM crosstab(\'select row_id, movie_name, cinema_name
from cinema_multiples_aev
order by 1,2\')
AS cinema_multiples_aev(row_id INT, %s);
', movie_columns, movie_columns);
ALTER TABLE movie_multiples DROP COLUMN row_id;
END
$$ LANGUAGE plpgsql;
SELECT *
FROM movie_multiples;
DROP TABLE IF EXISTS movie_multiples;
DROP TABLE IF EXISTS cinema_multiples_aev;
If it's confusing for the lack of strusture, everything could be found here github
I have a table A as:
Col1 Col2
1 D:\Akagane2\Source\SubModule\ExtractText.vb
2 D:\Akagane2\Source\SubModule\ExtractText.vb
I want select output a table has data as
Col1 Col2
1 ExtractText.vb
2 ExtractText.vb
Select in postgresql,
Can you help me ?
Something like
SELECT RIGHT('D:\Akagane2\Source\SubModule\ExtractText.vb', POSITION('\' in REVERSE('D:\Akagane2\Source\SubModule\ExtractText.vb')) -1 );
On PostgreSQL.
mole=> CREATE TABLE A (Col1 INTEGER, Col2 VARCHAR);
CREATE TABLE
mole=> INSERT INTO A VALUES (1, 'D:\Akagane2\Source\SubModule\ExtractText.vb');
INSERT 0 1
mole=> INSERT INTO A VALUES (2, 'D:\Akagane2\Source\SubModule\ExtractText.vb');
INSERT 0 1
mole=> SELECT * FROM A;
col1 | col2
------+---------------------------------------------
1 | D:\Akagane2\Source\SubModule\ExtractText.vb
2 | D:\Akagane2\Source\SubModule\ExtractText.vb
(2 rows)
mole=> SELECT Col1, REGEXP_REPLACE(Col2, '.*\\', '') AS col2 FROM A;
col1 | col2
------+----------------
1 | ExtractText.vb
2 | ExtractText.vb
(2 rows)
I've a product table with product_id and 100+ attributes. The product_id is text whereas the attribute columns are integer, i.e. 1 if the attribute exists. When the Postgresql crosstab is run, non-matching atrributes return null values. How do I replace nulls with zeros instead.
SELECT ct.*
INTO ct3
FROM crosstab(
'SELECT account_number, attr_name, sub FROM products ORDER BY 1,2',
'SELECT DISTINCT attr_name FROM attr_names ORDER BY 1')
AS ct(
account_number text,
Attr1 integer,
Attr2 integer,
Attr3 integer,
Attr4 integer,
...
)
Replace this result:
account_number Attr1 Attr2 Attr3 Attr4
1.00000001 1 null null null
1.00000002 null null 1 null
1.00000003 null null 1 null
1.00000004 1 null null null
1.00000005 1 null null null
1.00000006 null null null 1
1.00000007 1 null null null
with this below:
account_number Attr1 Attr2 Attr3 Attr4
1.00000001 1 0 0 0
1.00000002 0 0 1 0
1.00000003 0 0 1 0
1.00000004 1 0 0 0
1.00000005 1 0 0 0
1.00000006 0 0 0 1
1.00000007 1 0 0 0
A workaround would be to do a select account_number, coalesce(Attr1,0)... on the result. But typing out coalesce for each of the 100+ columns is rather unyieldly. Is there a way to handle this using crosstab? Thanks
You can use coalesce:
select account_number,
coalesce(Attr1, 0) as Attr1,
coalesce(Attr2, 0) as Attr2,
etc
if you can put those Attrs into a table like
attr
-----
Attr1
Attr2
Attr3
...
then you could automatically generate the repeating coalesce statement like
SELECT 'coalesce("' || attr || '", 0) "'|| attr ||'",' from table;
to save some typing.