More than one row error when populating a table - postgresql

I have create a table using create table and with these column:
create table myschema.mytable(
id serial PRIMARY KEY,
row_num integer,
col_num integer,
pix_centroid geometry,
pix_val double precision
)
When I am trying to populate it:
insert into pixelbased (id, row_num, col_num, pix_centroid, pix_val)
values (
DEFAULT,
(select((ST_PixelAsPolygons(rast, 1)).x) from mytable where rid=3),
(select((ST_PixelAsPolygons(rast, 1)).x) from mytable where rid=3),
(select(ST_Centroid((ST_PixelAsPolygons(rast, 1)).geom)) from rwanda8 where rid=3),
(select(ST_PixelAsPolygons(rast, 1)).val from mytable where rid=3)
)
I am encountered with the following error:
ERROR: more than one row returned by a subquery used as an expression.
I know that since I have more than one row for every column it does make sense to have such an error. But I really need to have all the columns calculated as mentioned. Anyone knows what should I do?
In fact I want to insert the result the following query in the table:
select
(ST_PixelAsPolygons(rast, 1)).val as geomval1,
(ST_PixelAsPolygons(rast, 1)).x as X,
(ST_PixelAsPolygons(rast, 1)).y as Y,
(ST_Centroid((ST_PixelAsPolygons(rast, 1)).geom)) as geom
from rwanda8
where rid=3
Anyone knows what should I do?

Just use the select query in instead of the values
insert into pixelbased (row_num, col_num, pix_centroid, pix_val)
select
(ST_PixelAsPolygons(rast, 1)).val as geomval1,
(ST_PixelAsPolygons(rast, 1)).x as X,
(ST_PixelAsPolygons(rast, 1)).y as Y,
(ST_Centroid((ST_PixelAsPolygons(rast, 1)).geom)) as geom
from rwanda8 where rid=3
Do not insert the id as it is a serial and will generate itself.

One of your subqueries returns more than 1 row. So use LIMIT 0,1 or something to get only one single value per subquery.
If you need more than 1 value per column, you should review your insert algorithm and use a cursor for instance.

Related

Update a column with row_number with duplicate records without PK

I have a table with duplicate records but without primary key. Data looks like this:
I want to update one empty column with one of column concatenate with row_number. After update, I want to achieve this:
Since the table does not have a unique column, which means I would join back to a CTE or subquery. I know in sql server, it can be done like this:
UPDATE X
SET X.NEW_KEY = X.PERSONNUMBER + '-' + X.NEW_CODE_DEST
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY PERSONNUMBER) AS NEW_CODE_DEST,PERSONNUMBER,NEW_KEY
FROM EMPLOYEE
) as X;
I tried same logic in postgresql but it didn't work. It threw an error:
SQL Error [42P01]: ERROR: relation "x" does not exist
I also tried this in
UPDATE EMPLOYEE
SET NEW_KEY = X.PERSONNUMBER || '-' || X.NEW_CODE_DEST
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY PERSONNUMBER) AS NEW_CODE_DEST,PERSONNUMBER
FROM EMPLOYEE
) as X;
The NEW_KEY is updated with duplicate value, not surprise there.
So is there a equivalent method in to achieve the same update result? Or I have my query wrong?
Really appreciate the help!

Computed table column with MAX value between rows containing a shared value

I have the following table
CREATE TABLE T2
( ID_T2 integer NOT NULL PRIMARY KEY,
FK_T1 integer, <--- foreign key to T1(Table1)
FK_DATE date, <--- foreign key to T1(Table1)
T2_DATE date, <--- user input field
T2_MAX_DIFF COMPUTED BY ( (SELECT DATEDIFF (day, MAX(T2_DATE), CURRENT_DATE) FROM T2 GROUP BY FK_T1) )
);
I want T2_MAX_DIFF to display the number of days since last input across all similar entries with a common FK_T1.
It does work, but if another FK_T1 values is added to the table, I'm getting an error about "multiple rows in singleton select".
I'm assuming that I need some sort of WHERE FK_T1 = FK_T1 of corresponding row. Is it possible to add this? I'm using Firebird 3.0.7 with flamerobin.
The error "multiple rows in singleton select" means that a query that should provide a single scalar value produced multiple rows. And that is not unexpected for a query with GROUP BY FK_T1, as it will produce a row per FK_T1 value.
To fix this, you need to use a correlated sub-query by doing the following:
Alias the table in the subquery to disambiguate it from the table itself
Add a where clause, making sure to use the aliased table (e.g. src, and src.FK_T1), and explicitly reference the table itself for the other side of the comparison (e.g. T2.FK_T1)
(optional) remove the GROUP BY clause because it is not necessary given the WHERE clause. However, leaving the GROUP BY in place may uncover certain types of errors.
The resulting subquery then becomes:
(SELECT DATEDIFF (day, MAX(src.T2_DATE), CURRENT_DATE)
FROM T2 src
WHERE src.FK_T1 = T2.FK_T1
GROUP BY src.FK_T1)
Notice the alias src for the table referenced in the subquery, the use of src.FK_T1 in the condition, and the explicit use of the table in T2.FK_T1 to reference the column of the current row of the table itself. If you'd use src.FK_T1 = FK_T1, it would compare with the FK_T1 column of src (as if you'd used src.FK_T1 = src.FK_T2), so that would always be true.
CREATE TABLE T2
( ID_T2 integer NOT NULL PRIMARY KEY,
FK_T1 integer,
FK_DATE date,
T2_DATE date,
T2_MAX_DIFF COMPUTED BY ( (
SELECT DATEDIFF (day, MAX(src.T2_DATE), CURRENT_DATE)
FROM T2 src
WHERE src.FK_T1 = T2.FK_T1
GROUP BY src.FK_T1) )
);

Convert rows to columns in SQL Server table

My table has the below sample data:
DECLARE #FHTable table (PK_ID int,FK_ID int,P_ID int, T_ID int, A_day int,A_hour TIME,D_day int,D_hour time)
INSERT INTO #FHtable VALUES (129,194,252,1005322,NULL,NULL,1,'02:30:00.0000000')
INSERT INTO #FHtable VALUES (130,194,311,1000891,3,'04:30:00.0000000',null,null)
INSERT INTO #FHtable VALUES (131,194,311,1000129,NULL,NULL,4,'03:30:00.0000000')
INSERT INTO #FHtable VALUES (132,194,252,1000025,6,'03:00:00.0000000',null,null)
SELECT * FROM #FHtable
My final result Should be of the below table:
DECLARE #FinalResultTable TABLE (FK_ID int,P_IDFrom int,P_IDTO INT, T_IDFrom int,T_IDTo INT, A_day int,A_hour TIME,D_day int,D_hour time)
INSERT INTO #FinalResultTable VALUES (194,252,311,1005322,1000891,3,'04:30:00.0000000',1,'02:30:00.0000000')
INSERT INTO #FinalResultTable VALUES (194,311,252,1000129,1000025,6,'03:00:00.0000000',4,'03:30:00.0000000')
select * from #FinalResultTable
The logic is there will be 4 rows for each FK_ID. The first and the second row is a source to destination and the 3rd and 4th row is again a source to destination.
Can you please help
As I pointed out in the comments, it's not very clear what is the logic that joins each arrival with the corresponding departure.
What it's clear to me it's that your title is wrong: you are not talking about converting rows to columns. Instead, you are just grouping 2 by 2 the rows of the table. So, you have 2 approaches, using a GROUP BY or just using a JOIN, it depends on what is exactly your logic.
Here is an example:
select A.FK_ID,
A.PK_ID as P_IDFrom, B.PK_ID as P_IDTO,
A.T_ID as T_IDFrom int, B.T_ID as T_IDTo,
B.A_day, B.A_hour,
A.D_day, A.D_hour
from #FHTable A
join #FHTable B on B.PK_ID+1=A.PK_ID
where A.A_day is null
(this assumes that an arrivals's PK is always +1 w.r.t. its departure).

Can the categories in the postgres tablefunc crosstab() function be integers?

It's all in the title. Documentation has something like this:
SELECT *
FROM crosstab('...') AS ct(row_name text, category_1 text, category_2 text);
I have two tables, lab_tests and lab_tests_results. All of the lab_tests_results rows are tied to the primary key id integer in the lab_tests table. I'm trying to make a pivot table where the lab tests (identified by an integer) are row headers and the respective results are in the table. I can't get around a syntax error at or around the integer.
Is this possible with the current set up? Am I missing something in the documentation? Or do I need to perform an inner join of sorts to make the categories strings? Or modify the lab_tests_results table to use a text identifier for the lab tests?
Thanks for the help, all. Much appreciated.
Edit: Got it figured out with the help of Dmitry. He had the data layout figured out, but I was unclear on what kind of output I needed. I was trying to get the pivot table to be based on batch_id numbers in the lab_tests_results table. Had to hammer out the base query and casting data types.
SELECT *
FROM crosstab('SELECT lab_tests_results.batch_id, lab_tests.test_name, lab_tests_results.test_result::FLOAT
FROM lab_tests_results, lab_tests
WHERE lab_tests.id=lab_tests_results.lab_test AND (lab_tests.test_name LIKE ''Test Name 1'' OR lab_tests.test_name LIKE ''Test Name 2'')
ORDER BY 1,2'
) AS final_result(batch_id VARCHAR, test_name_1 FLOAT, test_name_2 FLOAT);
This provides a pivot table from the lab_tests_results table like below:
batch_id |test_name_1 |test_name_2
---------------------------------------
batch1 | result1 | <null>
batch2 | result2 | result3
If I understand correctly your tables look something like this:
CREATE TABLE lab_tests (
id INTEGER PRIMARY KEY,
name VARCHAR(500)
);
CREATE TABLE lab_tests_results (
id INTEGER PRIMARY KEY,
lab_tests_id INTEGER REFERENCES lab_tests (id),
result TEXT
);
And your data looks something like this:
INSERT INTO lab_tests (id, name)
VALUES (1, 'test1'),
(2, 'test2');
INSERT INTO lab_tests_results (id, lab_tests_id, result)
VALUES (1,1,'result1'),
(2,1,'result2'),
(3,2,'result3'),
(4,2,'result4'),
(5,2,'result5');
First of all crosstab is part of tablefunc, you need to enable it:
CREATE EXTENSION tablefunc;
You need to run it one per database as per this answer.
The final query will look like this:
SELECT *
FROM crosstab(
'SELECT lt.name::TEXT, lt.id, ltr.result
FROM lab_tests AS lt
JOIN lab_tests_results ltr ON ltr.lab_tests_id = lt.id'
) AS ct(test_name text, result_1 text, result_2 text, result_3 text);
Explanation:
The crosstab() function takes a text of a query which should return 3 columns; (1) a column for name of a group, (2) a column for grouping, (3) the value. The wrapping query just selects all the values those crosstab() returns and defines the list of columns after (the part after AS). First is the category name (test_name) and then the values (result_1, result_2). In my query I'll get up to 3 results. If I have more then 3 results then I won't see them, If I have less then 3 results I'll get nulls.
The result for this query is:
test_name |result_1 |result_2 |result_3
---------------------------------------
test1 |result1 |result2 |<null>
test2 |result3 |result4 |result5

function st_intersects() does not exist using PostgreSQL 9.3

I have a table as shown below with three columns.
Table: geomet
create table geomet
(
cola float,
colb float,
geopath geometry
);
Insertion of records:
insert into geomet values('12.32232442','43.2324535',point(12.32232442,43.2324535)::geometry);
I have this:
select * from geomet;
cola colb geopath
---------------------------------------------------------------------
12.32232442 43.2324535 01010000004F34D5B407A528409D2B4A09C19D4540
Note: I need to find the Intersect of column geopath in my table as shown above. I have also installed PostGIS.
So I have tried this:
Try 1:
SELECT ST_Intersects(geopath) from geomet;
Got an error:
ERROR: function st_intersects(geometry) does not exist
Try 2:
SELECT ST_Intersects(cola,colb) from geomet;
Got an error:
ERROR: function st_intersects(double precision, double precision) does not exist
ST_Intersects requires two geometries or two geometry fields, as you are checking for the intersection between one geometry and another or one set and another set.
SELECT ST_Intersects(geomA, geomB) FROM some_table;
or
SELECT a.id., b.id, ST_Intersects(a.geom, b.geom)
FROM table a, table b
WHERE a.id > b.id;
will give you all the pairwise intersection between two tables. Obviously, you can use ST_Intersects in a where clause in a similar fashion, to only return rows where the records from two different tables intersect.