2

So I am trying to copy values from temporary tables into a table with an array type and I get the following error which I can't find a way to overcome.

ERROR:  cannot assign to field "addressid" of column "address" because its type addresstype[] is not a composite type
LINE 32: ...nID,Person.Title,Person.FirstName,Person.LastName,Person.Add...
                                                              ^
********** Error **********

ERROR: cannot assign to field "addressid" of column "address" because its type addresstype[] is not a composite type
SQL state: 42804

Here is the query I try:

INSERT INTO Customer (CustomerID,Person.PersonID,Person.Title,Person.FirstName,Person.LastName,Person.Address.AddressID,Person.Address.AddressLine1,Person.Address.AddressLine2,Person.Address.City,Person.Address.PostalCode)
SELECT TCustomer.CustomerID,TPerson.PersonID,TPerson.Title,TPerson.FirstName,TPerson.LastName,TAddress.AddressID,TAddress.AddressLine1,TAddress.AddressLine2,TAddress.City,TAddress.PostalCode
FROM TCustomer,TPerson,TPersonAddress,TAddress
WHERE TCustomer.PersonID = TPerson.PersonID
AND TPerson.PersonID = TPersonAddress.PersonID
AND TPersonAddress.AddressID = TAddress.AddressID;

tables&types:

CREATE TYPE AddressType AS(
AddressID integer,
AddressLine1 text,
AddressLine2 text,
City text,
PostalCode text);

CREATE TYPE PersonType AS(
PersonID integer, 
Title text,
FirstName text,
LastName text,
Address AddressType[]);

CREATE TABLE Customer(
CustomerID integer,
Person PersonType,
PRIMARY KEY(CustomerID));

CREATE TEMPORARY TABLE TAddress (AddressID integer,AddressLine1 text,AddressLine2 text,City text,PostalCode text);
CREATE TEMPORARY TABLE TPerson (PersonID integer,Title text,FirstName text,LastName text);
CREATE TEMPORARY TABLE TCustomer (CustomerID integer,PersonID integer);
CREATE TEMPORARY TABLE TPersonAddress (PersonID integer,AddressID integer);

Temporary tables have copied data from CSV files. The problem is that each person can have many different Addresses. So I have to load all the address to an AddressType Array. But that seems impossible right now and I can't find a way to handle the array.

All I found around the internet to handle the array is:

INSERT INTO aa VALUES (3, ARRAY[ARRAY[1,2],ARRAY[3,4]]);
INSERT INTO sal_emp
    VALUES ('Bill',
    '{10000, 10000, 10000, 10000}',
    '{{"meeting", "lunch"}, {"training", "presentation"}}');

And such but these example just add specified values that don't help with my case at all. So how to I make it copy values from: TAddress.AddressID,TAddress.AddressLine1,TAddress.AddressLine2,TAddress.City,TAddress.PostalCode

to array:

Person.Address.AddressID,Person.Address.AddressLine1,Person.Address.AddressLine2,Person.Address.City,Person.Address.PostalCode

And if it's the same person it has to add all the addresses to the same array.

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
DieCriminal
  • 59
  • 1
  • 1
  • 8
  • Are you at liberty to improve the table layout? Because I am going to suggest [database normalization](https://en.wikipedia.org/wiki/Database_normalization). – Erwin Brandstetter Jan 27 '15 at 16:31
  • Yes I will consider any suggetion. Although note that the point is to make a relational Database to Object-Relational Database. So I don't want to split the tables so much that I end up with the same relational database schema.I know Object-Retional Database sucks but what can you do, I need to do it like that.(the temporary tables are what the relational database tables look like i guess you can figure out pkeys/fkeys) – DieCriminal Jan 27 '15 at 16:45
  • And I'd like to skip table PersonAddress atleast, with types/arrays etc so it makes some difference from relational database. – DieCriminal Jan 27 '15 at 16:46
  • The link you provided talks about relational model mainly. – DieCriminal Jan 27 '15 at 16:50
  • I disagree, arrays of composite types are are a rational and useful choice for rows are are just sets of vectors. – Rol Nov 25 '21 at 10:31

2 Answers2

7

About the error

While it is possible to assign subfields of composite / row types like this:

CREATE TYPE foo AS (a int, b text);
CREATE TABLE bar (c int, f foo);

Both work:

INSERT INTO bar(a,b) SELECT 1, '(1,foo)';
INSERT INTO bar(a,b.a, b.b) SELECT 2, 2, 'bar';

The same is not possible with arrays (so neither for arrays of composite types).

CREATE TABLE baz (a int, b foo[]);

Not possible:

INSERT INTO baz(a,b.a, b.b) SELECT 2, 2, 'bar';

Must be one of:

INSERT INTO baz(a,b) SELECT 1, '{"(1,foo)"}';
INSERT INTO baz(a,b) SELECT 1, ARRAY['(1,foo)'];

Database design

I'm going to suggest a complete redesign with normalized tables.

Arrays of composite types are very unwieldy, don't save any disk space, disable many of the core features for indexing or relational integrity and are really an exotic use of the options provided by Postgres.

I would not operate with composite column types. I know you commented that you "need" to do it like that, but I don't see the point. Just because you can do it, doesn't mean it should be done. Use more or less normalized tables instead. The simple case with two tables:

CREATE TABLE person (
  person_id serial PRIMARY KEY
, title text
, firstname text
, lastname text
);

CREATE TABLE address (
  address_id serial PRIMARY KEY
, person_id int REFERENCES person
, addressline1 text
, addressline2 text
, city text
, postalcode text
);

A person can have many addresses, an address belongs to a single person. In reality, addresses are used by multiple persons, but it's often simpler to add a few addresses redundantly. If you have many like that, use a many-to-many implementation with an additional table person_address. Details here:

Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • I figured out a better solution: Person -> table refers to PersonAddress -> table has type (instead of address ID) Address. No need for array, and still has OR-database elements What do you think? – DieCriminal Jan 27 '15 at 17:39
  • 2
    @DieCriminal: I added a bit concerning the db design. – Erwin Brandstetter Jan 28 '15 at 05:32
4

At least on PostgreSQL 10, you can do this:

CREATE TYPE Child AS ( Name VARCHAR(10), Age INTEGER );

CREATE TABLE Father ( Name VARCHAR(10), Children Child[] );

INSERT INTO Father(Name, Children) VALUES (
    'Thanos',
    ARRAY[
        ('Nebula', 25)::Child,
        ('Gamora', 28)::Child
    ]
)

The nice aspect of this solution is that if you have VARCHAR in your TYPE, you don't need to deal with escape sequences.

fernacolo
  • 7,012
  • 5
  • 40
  • 61
  • 1
    Is there a way to update 'Nebula' to 'Tony Stark' without rewriting the entire `ROW('Tony Stark', 25) :: Child` – user1720827 Apr 15 '22 at 06:34