How to select data with multiple row output from 1 row (PostgreSQL) - postgresql

I have data on table test like this
customer_no | name | chance
---------------------------
00000000001 | AAAA | 3
00000000002 | BBBB | 2
00000000003 | CCCC | 1
Now,i want to select from table test with multiple output that calculate by value of field chance
output like this
customer_no | name
------------------
00000000001 | AAAA
00000000001 | AAAA
00000000001 | AAAA
00000000002 | BBBB
00000000002 | BBBB
00000000003 | CCCC
how to select command in pgsql database?

Try this:
SELECT customer_no, name FROM (
SELECT test.*,
generate_series(1,chance) i
FROM test
) test;
Here is a demo.

with recursive CTE_nums as (
select max(chance) as num from test
union all
select num - 1 from CTE_nums
where num > 1
)
select
t.customer_no, t.name
from test as t
inner join CTE_nums as n on n.num <= t.chance
order by 1, 2
SQL FIDDLE EXAMPLE

Related

Postgres Insert N Rows in a Loop for All Values in a Selected Column

Suppose I have users stored as
select * from users_t where user_name like 'ABC%';
id user_name
1 ABC1
2 ABC2
.. ..
Now I need to loop through all user_name's and make that number of INSERTs into a different table, RECALLS_T. All the other columns are hard-coded constants that I define.
Assume the following table, with a Sequence called RECALLS_T_ID_SEQ on the ID:
id created_by_user_name field1 field2
1 ABC1 Const1 Const2
2 ABC2 Const1 Const2
.. .. .. ..
How do I insert these in a Postgres loop?
ADDITIONAL QUESTION Also, what if I need to insert X (say 5) Recalls for each User entry? Suppose it's not a 1:1 mapping, but 5:1, where 5 is a hard-coded loop number.
You can use the select in the insert statement:
insert into recalls_t (created_by_user_name, field1, field2)
select user_name, 'Const1', 'Const2'
from users_t
where user_name like 'ABC%';
Use the function generate_series() to insert more than one row for each entry from users_t. I have added the column step to illustrate this:
insert into recalls_t (created_by_user_name, field1, field2, step)
select user_name, 'Const1', 'Const2', step
from users_t
cross join generate_series(1, 3) as step
where user_name like 'ABC%'
returning *
id | created_by_user_name | field1 | field2 | step
----+----------------------+--------+--------+------
1 | ABC1 | Const1 | Const2 | 1
2 | ABC2 | Const1 | Const2 | 1
3 | ABC1 | Const1 | Const2 | 2
4 | ABC2 | Const1 | Const2 | 2
5 | ABC1 | Const1 | Const2 | 3
6 | ABC2 | Const1 | Const2 | 3
(6 rows)
Live demo in Db<>fiddle.

Postgresql use more than one row as expression in sub query

As the title says, I need to create a query where I SELECT all items from one table and use those items as expressions in another query. Suppose I have the main table that looks like this:
main_table
-------------------------------------
id | name | location | //more columns
---|------|----------|---------------
1 | me | pluto | //
2 | them | mercury | //
3 | we | jupiter | //
And the sub query table looks like this:
some_table
---------------
id | item
---|-----------
1 | sub-col-1
2 | sub-col-2
3 | sub-col-3
where each item in some_table has a price which is in an amount_table like so:
amount_table
--------------
1 | 1000
2 | 2000
3 | 3000
So that the query returns results like this:
name | location | sub-col-1 | sub-col-2 | sub-col-3 |
----------------------------------------------------|
me | pluto | 1000 | | |
them | mercury | | 2000 | |
we | jupiter | | | 3000 |
My query currently looks like this
SELECT name, location, (SELECT item FROM some_table)
FROM main_table
INNER JOIN amount_table WHERE //match the id's
But I'm running into the error more than one row returned by a subquery used as an expression
How can I formulate this query to return the desired results?
you should decide on expected result.
to get one-tp-many relation:
SELECT name, location, some_table.item
FROM main_table
JOIN some_table on true -- or id if they match
INNER JOIN amount_table --WHERE match the id's
to get one-to-one with all rows:
SELECT name, location, (SELECT array_agg(item) FROM some_table)
FROM main_table
INNER JOIN amount_table --WHERE //match the id's

Resolve many to many relationship in SQL

I'm using Postgresql. Let's say I have 3 tables:
Classes
id | name
1 | Biology
2 | Math
Students
id | name
1 | John
2 | Jane
Student_Classes
id | student_id | class_id | registration_token
1 | 1 | 1 | abc
2 | 1 | 2 | def
3 | 2 | 1 | zxc
I want to obtain a result set like this:
Results
student_name | biology | math
John | abc | def
Jane | zxc | NULL
I can get this result set with this query:
SELECT
student.name as student_name,
biology.registration_token as biology,
math.registration_token as math
FROM
Students
LEFT JOIN (
SELECT registration_token FROM Student_Classes WHERE class_id = (
SELECT id FROM Classes WHERE name = 'Biology'
)
) AS biology
ON Students.id = biology.student_id
LEFT JOIN (
SELECT registration_token FROM Student_Classes WHERE class_id = (
SELECT id FROM Classes WHERE name = 'Math'
)
) AS math
ON Students.id = math.student_id
Is there a way to get this same result set without having a join statement for each class? With this solution, if I want to add a class, I need to add another join statement.
You can do this via postgresql tablefunc extension crosstab but such presentation requirements may be handled better outside of sql.

T-SQL: Rows to Columns With Count

Let me draw up the table first (there are dozens of columns and dozens of values under Code in reality)
Code | Pat | Col1 | Col2 | Col3
---------------------------------
ABC | 001 | | XX | Q1
ABC | 002 | xx | xx | Q1
ABC | 003 | xx | xxx | Q1
DEF | 004 | xx | xx | Q1
DEF | 005 | xx | xx | Q1
DEF | 006 | xx | xxx | Q1
The resulting table need to look like
ABC | DEF
---------
2 | 3
3 | 3
Let me try and explain. For each 'Code' column, I would need to count the number of entries in Col1 to ColX where the cell is not null/empty.
So in example above, Code ABC has a count of 2 in Col1 and a count of 3 in Col2 Similarly for DEF, both have a count of 3
I've tried lots of things but got to the point where I'm now looking at a blank page again!
ALTERNATIVELY
Code | Col1 | Col2
--------------------
ABC | 2 | 3
DEF | 3 | 3
Please advise
The alternative solution can be reached by using GROUP BY and summing up a calculated number:
SELECT
[Code],
SUM(CASE WHEN ISNULL(Col1, '') = '' THEN 0 ELSE 1 END) as [Col1],
SUM(CASE WHEN ISNULL(Col2, '') = '' THEN 0 ELSE 1 END) as [Col2],
...
FROM T
GROUP by [Code]

SQL - group by - limit clause - postgresql

I have a table which has two columns C1 and C2.
C1 has an integer data type and C2 has text.
Table looks like this.
---C1--- ---C2---
1 | a |
1 | b |
1 | c |
1 | d |
1 | e |
1 | f |
1 | g |
2 | h |
2 | i |
2 | j |
2 | k |
2 | l |
2 | m |
2 | n |
------------------
My question: i want a sql query which does group by on column C1 but with size of 3.
looks like this.
------------------
1 | a,b,c |
1 | d,e,f |
1 | g |
2 | h,i,j |
2 | k,l,m |
2 | n |
------------------
is it possible by executing SQL???
Note: I do not want to write stored procedure or function...
You can use a common table expression to partition the results into rows, and then use STRING_AGG to join them into comma separated lists;
WITH cte AS (
SELECT *, (ROW_NUMBER() OVER (PARTITION BY C1 ORDER BY C2)-1)/3 rn
FROM mytable
)
SELECT C1, STRING_AGG(C2, ',') ALL_C2
FROM cte
GROUP BY C1,rn
ORDER BY C1
An SQLfiddle to test with.
A short explanation of the common table expression;
ROW_NUMBER() OVER (...) will number the results from 1 to n for each value of C1. We then subtract 1 and divide by 3 to get the sequence 0,0,0,1,1,1,2,2,2... and group by that value in the outer query to get 3 results per row.
Apart from Joachim Isaksson's answer,you try this method also
SELECT C1, string_agg(C2, ',') as c2
FROM (
SELECT *, (ROW_NUMBER() OVER (PARTITION BY C1 ORDER BY C2)-1)/3 as row_num
FROM atable) t
GROUP BY C1,row_num
ORDER BY c2