I have following SQL table data:
postgres=# SELECT * from data
postgres-# ;
a | b | c
----+-----------+-------------------
10 | some_text | some_text
11 | some_text | some_text
12 | some_text | some_another_text
13 | some_text | some_another_text
(4 rows)
I also have tmp table, which was created so I could update data from it:
postgres=# SELECT * FROM tmp
postgres-# ;
a | b | c
----+-----------+-----------
21 | some_text | some_text
22 | some_text | some_text
(2 rows)
So I want to update data with tmp table with following command:
UPDATE data SET a = tmp.a, b = tmp.b, c = tmp.c FROM tmp WHERE data.c = 'some_another_text';
I am expecting following output:
postgres=# SELECT * from data
postgres-# ;
a | b | c
----+-----------+-------------------
10 | some_text | some_text
11 | some_text | some_text
21 | some_text | some_text
22 | some_text | some_text
(4 rows)
Note that fields 3 and 4 have values of a 21 and 22 respectively.
I get following output and I don't know why or even if I am doing it correctly:
postgres=# SELECT * FROM data;
a | b | c
----+-----------+-----------
10 | some_text | some_text
11 | some_text | some_text
21 | some_text | some_text
21 | some_text | some_text
Related
We use Postgresql 14.1
I have a sample data that contains over 50 million records.
base table:
+------+----------+--------+--------+--------+
| id | item_id | battles| wins | damage |
+------+----------+--------+--------+--------+
| 1 | 255 | 35 | 52.08 | 1245.2 |
| 2 | 255 | 35 | 52.08 | 1245.2 |
| 3 | 255 | 35 | 52.08 | 1245.3 |
| 4 | 255 | 35 | 52.08 | 1245.3 |
| 5 | 255 | 35 | 52.09 | 1245.4 |
| 6 | 255 | 35 | 52.08 | 1245.3 |
| 7 | 255 | 35 | 52.08 | 1245.3 |
| 8 | 255 | 35 | 52.08 | 1245.7 |
| 1 | 460 | 18 | 47.35 | 1010.1 |
| 2 | 460 | 27 | 49.18 | 1518.9 |
| 3 | 460 | 16 | 50.78 | 1171.2 |
+------+----------+--------+--------+--------+
We need to get the target row number and 2 next and 2 previous rows as quickly as possible.
Indexed columns:
id
item_id
Sorting:
damage (DESC)
wins (DESC)
battles (ASC)
id (ASC)
At the example, we need to find the row number and +- 2 rows where id = 4 and item_id = 255. The result table should be:
+------+----------+--------+--------+--------+------+
| id | item_id | battles| wins | damage | rank |
+------+----------+--------+--------+--------+------+
| 5 | 255 | 35 | 52.09 | 1245.4 | 2 |
| 3 | 255 | 35 | 52.08 | 1245.3 | 3 |
| 4 | 255 | 35 | 52.08 | 1245.3 | 4 |
| 6 | 255 | 35 | 52.08 | 1245.3 | 5 |
| 7 | 255 | 35 | 52.08 | 1245.3 | 6 |
+------+----------+--------+--------+--------+------+
How can I do this with Row number windows function?
Is there is any way optimize in query to make it faster because other columns have no indexes?
CREATE OR REPLACE FUNCTION find_top(in_id integer, in_item_id integer) RETURNS TABLE (
r_id int,
r_item_id int,
r_battles int,
r_wins real,
r_damage real,
r_rank bigint,
r_eff real,
r_frags int
) AS $$
DECLARE
center_place bigint;
BEGIN
SELECT place INTO center_place FROM
(SELECT
id, item_id,
ROW_NUMBER() OVER (ORDER BY damage DESC, wins DESC, battles, id) AS place
FROM
public.my_table
WHERE
item_id = in_item_id
AND battles >= 20
) AS s
WHERE s.id = in_id;
RETURN QUERY SELECT
s.place, pt.id, pt.item_id, pt.battles, pt.wins, pt.damage
FROM
(
SELECT * FROM
(SELECT
ROW_NUMBER () OVER (ORDER BY damage DESC, wins DESC, battles, id) AS place,
id, item_id
FROM
public.my_table
WHERE
item_id = in_item_id
AND battles >= 20) x
WHERE x.place BETWEEN (center_place - 2) AND (center_place + 2)
) s
JOIN
public.my_table pt
ON pt.id = s.id AND pt.item_id = s.item_id;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION find_top(in_id integer, in_item_id integer) RETURNS TABLE (
r_id int,
r_item_id int,
r_battles int,
r_wins real,
r_damage real,
r_rank bigint,
r_eff real,
r_frags int
) AS $$
BEGIN
RETURN QUERY
SELECT c.*, B.ord -3 AS row_number
FROM
( SELECT array_agg(id) OVER w AS id
, array_agg(item_id) OVER w AS item_id
FROM public.my_table
WINDOW w AS (ORDER BY damage DESC, wins DESC, battles, id ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING)
) AS a
CROSS JOIN LATERAL unnest(a.id, a.item_id) WITH ORDINALITY AS b(id, item_id, ord)
INNER JOIN public.my_table AS c
ON c.id = b.id
AND c.item_id = b.item_id
WHERE a.item_id[3] = in_item_id
AND a.id[3] = in_id
ORDER BY b.ord ;
END ; $$ LANGUAGE plpgsql;
test result in dbfiddle
Hello I have created a view, but want to pivot it.
OUTPUT before pivoting:
tag1 | qmonth1 | qmonth2 | sum1
--------+-----------+-----------+--------
name1 | 18-05 | MAY | -166
name2 | 18-05 | MAY | -86
name3 | 18-05 | MAY | 35
name1 | 18-06 | JUN | -102
name2 | 18-06 | JUN | -32
name3 | 18-06 | JUN | -75
name1 | 18-09 | AVG | -135
name2 | 18-09 | AVG | -52
name3 | 18-09 | AVG | -17
expected output:
qmonth2 | name1 | name2 | name3
--------+-------+-------+-------
MAY | -166 | -86 | 35
JUN | -102 | -32 | -75
AVG | -135 | -52 | -17
my full query:
SELECT tag1,qmonth2,sum1 FROM crosstab
('SELECT tag1::text,qmonth1,qmonth2::text,sum1::numeric
FROM public."chartdata_chart3"')
AS ct ( "tag1" TEXT,"qmonth2" TEXT,"sum1" NUMERIC);
I getting this error and unable to resolve:
ERROR: invalid source data SQL statement
DETAIL: The provided SQL must return 3 columns: rowid, category, and values.
SQL state: 22023
SQL statement passed as paremeter to crosstab() function must return one row_name column, one category column, and one value column. This in your case is qmonth2, tag1 and sum1.
This is your query considering sum1 is an integer, qmonth2 and tag1 are text:
select *
from crosstab(
'select qmonth2, tag1, sum1
from public."chartdata_chart3"
') AS ct(qmonth2 text, name1 int, name2 int, name3 int;
Output:
qmonth2 | name1 | name2 | name3
---------+-------+-------+-------
MAY | -166 | -86 | 35
JUN | -102 | -32 | -75
AVG | -135 | -52 | -17
Refer to manual for more information and samples on how crosstab works.
Also, keep in mind that naming your columns/tables with quotes makes it more complex to write every SQL query so you are better off without doing that :)
I have table with the following data:
id | parent_id | short_name
----+-----------+----------------
6 | 5 | cpu
7 | 5 | ram
14 | 9 | tier-a
15 | 9 | rfc1918
16 | 9 | tolerant
17 | 9 | nononymous
13 | 12 | cloudstack
5 | 13 | virtualmachine
8 | 13 | volume
9 | 13 | ipv4
3 | | domain
4 | | account
12 | | vdc
(13 rows)
with recursive query it looks like this:
with recursive tree ( id, parent_id, short_name, deep_name ) as (
select resource_type_id, parent_resource_type_id, short_name, short_name::text
from resource_type
where parent_resource_type_id is null
union all
select rt.resource_type_id as id, rt.parent_resource_type_id, rt.short_name,
tree.deep_name || '.' || rt.short_name
from tree, resource_type rt
where tree.id = rt.parent_resource_type_id
)
select * from tree;
id | parent_id | short_name | deep_name
----+-----------+----------------+-----------------------------------
4 | | account | account
3 | | domain | domain
12 | | vdc | vdc
13 | 12 | cloudstack | vdc.cloudstack
9 | 13 | ipv4 | vdc.cloudstack.ipv4
5 | 13 | virtualmachine | vdc.cloudstack.virtualmachine
8 | 13 | volume | vdc.cloudstack.volume
6 | 5 | cpu | vdc.cloudstack.virtualmachine.cpu
15 | 9 | rfc1918 | vdc.cloudstack.ipv4.rfc1918
17 | 9 | nononymous | vdc.cloudstack.ipv4.nononymous
16 | 9 | tolerant | vdc.cloudstack.ipv4.tolerant
14 | 9 | tier-a | vdc.cloudstack.ipv4.tier-a
7 | 5 | ram | vdc.cloudstack.virtualmachine.ram
(13 rows)
How to fix the query so in result I get only leafs? eg. vdc.cloudstack.volume row and no vdc, vdc.cloudstack rows
UPD
rows with no children
Exclude the rows where deep_name has a superstring somewhere else in the table:
WITH RECURSIVE tree AS (...)
SELECT * FROM tree AS t1
WHERE NOT EXISTS (
SELECT 1 FROM tree AS t2
WHERE t2.deep_name
LIKE t1.deep_name || '.%'
);
Laurenz Albe's answer give me an idea. I think it would be more efficient to count childs than working with strings.
My solution is:
WITH RECURSIVE tree AS (...)
SELECT * FROM tree t1
WHERE not EXISTS ( SELECT 1 FROM tree t2 WHERE t1.id = t2.parent_id );
A leaf node is a child which is not itself a parent.
If all you want is a list of leaf notes you don't need the recursive CTE, you just need an anti-join in your preferred format.
If (as I imagine you do) you need the deep_name, I would anti-join the result of the recursive CTE to the raw source table on id = parent_id.
WITH RECURSIVE tree AS (...)
SELECT * FROM tree AS t1
WHERE NOT EXISTS (SELECT 1 FROM resource_type AS t2
WHERE t2.parent_resource_type_id = t1.id);
I have 2 tables that I want match by ID.
EDIT: Table1:
| id | other columns A |
| 23 | ... |
| 27 | ... |
| 9 | ... |
| 50 | ... |
Table2:
| id_new | id_old | other columns B
| 23 | 7 | ...
| 27 | 8 | ...
| 33 | 9 | ...
Problem is that the second table contains 2 ID columns: first with new ID second with the old one - both can match the ID from first table.
EDIT: there are some rows from table A which ID not match neither id_new nor id_old. But I want them to retain in the new table.
This is my desired result:
| id | id_new | id_old | other columns A + B
| 23 | 23 | 7 | A + B
| 27 | 27 | 8 | A + B
| 9 | 33 | 9 | A + B
| 50 | -- | -- | A
I tried this one but it's a huge dataset and my query takes a long time to execute.
create table spoj2
as
select *
from table1
left join table2 on table1.id = table2.id_new
or table1.id = table2.id_old
Is this what you need?
select table1.id, IFNULL(T2A.id_new, T2B.id_new) as id_new
, IFNULL(T2A.id_old, T2B.id_old) as id_old
FROM table1
LEFT JOIN table2 as T2A ON table1.id = T2A.id_new
LEFT JOIN table2 as T2B ON table1.id = T2B.id_old
WITH t1(id,o_t1) AS ( VALUES
(23,'...'),
(27,'...'),
(9,'...')
), t2(id_new,id_old,o_t2) AS ( VALUES
(23,7,'...'),
(27,8,'...'),
(33,9,'...')
)
SELECT t1.id,t2.id_new,t2.id_old,t1.o_t1,t2.o_t2 FROM t1
INNER JOIN t2 ON t2.id_new = t1.id
UNION ALL
SELECT t1.id,t2.id_new,t2.id_old,t1.o_t1,t2.o_t2 FROM t1
INNER JOIN t2 ON t2.id_old = t1.id;
Result:
id | id_new | id_old | o_t1 | o_t2
----+--------+--------+------+------
23 | 23 | 7 | ... | ...
27 | 27 | 8 | ... | ...
9 | 33 | 9 | ... | ...
(3 rows)
I am working with PostgreSQL 9.3, and I have this:
PARENT_TABLE
ID | NAME
1 | N_A
2 | N_B
3 | N_C
CHILD_TABLE
ID | PARENT_TABLE_ID | KEY | VALUE
1 | 1 | K_A | V_A
2 | 1 | K_B | V_B
3 | 1 | K_C | V_C
5 | 2 | K_A | V_D
6 | 2 | K_C | V_E
7 | 3 | K_A | V_F
8 | 3 | K_B | V_G
9 | 3 | K_C | V_H
Note that I might add K_D in KEY's, it's completely dynamic.
What I want is a query that returns me the following:
QUERY_TABLE
ID | NAME | K_A | K_B | K_C | others K_...
1 | N_A | V_A | V_B | V_C | ...
2 | N_B | V_D | | V_E | ...
3 | N_C | V_F | V_G | V_H | ...
Is this possible to do ? If so, how ?
Since there can be values missing, you need the "safe" form of crosstab() with the column names as second parameter:
SELECT * FROM crosstab(
'SELECT p.id, p.name, c.key, c."value"
FROM parent_table p
LEFT JOIN child_table c ON c.parent_table_id = p.id
ORDER BY 1'
,$$VALUES ('K_A'::text), ('K_B'), ('K_C')$$)
AS t (id int, name text, k_a text, k_b text, k_c text; -- use actual data types
Details in this related answer:
PostgreSQL Crosstab Query
About adding "extra" columns:
Pivot on Multiple Columns using Tablefunc