2017-12-03 16 views
1

사용자 정의 유형 (ingredient)의 배열 인 열이있는 행을 삽입하려고합니다. 내 테이블은 다음과 같습니다 원시 SQL을 사용하여사용자 정의 유형의 배열을 포스트 그레스에 삽입

CREATE TYPE ingredient AS (
    name text, 
    quantity text, 
    unit text 
); 

CREATE TABLE IF NOT EXISTS recipes (
    recipe_id uuid PRIMARY KEY DEFAULT gen_random_uuid(), 
    name text, 
    ingredients ingredient[], 
    // ... 
); 

, 나는하여 행을 삽입 할 수 있습니다

INSERT INTO recipes (name, ingredients) VALUES ('some_name', ARRAY[ROW('aa', 'bb', 'cc'), ROW('xx', 'yy', 'zz')]::ingredient[] );

하지만 난 pq lib 디렉토리로 이동이 작업을 수행하기 위해 고군분투하고있다. 나는 pq.Array 인터페이스를 만들었습니다

type Ingredient struct { 
    Name string 
    Quantity string 
    Unit string 
} 

type Ingredients []*Ingredient 

func (ings *Ingredients) ConvertValue(v interface{}) (driver.Value, error) { 
    return "something", nil 
} 
func (ings *Ingredients) Value() (driver.Value, error) { 
    val := `ARRAY[]` 
    for i, ing := range ings { 
     if i != 0 { 
      val += "," 
     } 
     val += fmt.Printf(`ROW('%v','%v','%v')`, ing.Name, ing.Quantity, ing.Unit) 
    } 
    val += `::ingredient[]` 
    return val, nil 
} 


// and then trying to insert via: 
stmt := `INSERT INTO recipes (
     name, 
     ingredients 
    ) 
    VALUES ($1, $2) 
` 
_, err := db.Exec(stmt, 
    "some_name", 
    &Ingredients{ 
     &Ingredient{"flour", "3", "cups"}, 
    }, 
) 

을하지만 페이지 오류 던지는 유지 :

Error insertingpq: malformed array literal: "ARRAY[ROW('flour','3','cups')]::ingredient[]" 

내가 잘못된 driver.Value을 반환 오전?

+0

'driver.Value'에서 변환을 생략하고 쿼리에 추가하려고 했습니까? 예 : '$ 2 :: ingredients []'. – mkopriva

+0

위와 동일한 오류가 발생했습니다. –

답변

1

당신은이 방법을 사용할 수 있습니다 여기에 대략 설명 : https://github.com/lib/pq/issues/544

type Ingredient struct { 
    Name string 
    Quantity string 
    Unit string 
} 

func (i *Ingredient) Value() (driver.Value, error) { 
    return fmt.Sprintf("('%s','%s','%s')", i.Name, i.Quantity, i.Unit), nil 
} 

stmt := `INSERT INTO recipes (name, ingredients) VALUES ($1, $2::ingredient[])` 

db.Exec(stmt, "some_name", pq.Array([]*Ingredient{{"flour", "3", "cups"}})) 

아니면 테이블의 레코드가 당신이 그것을 조회하는 경우, 당신은 아마 당신보다 삽입 중에 모방 할 수는 문자 형태의 성분 배열을 볼 수 있습니다.

func (ings *Ingredients) Value() (driver.Value, error) { 
    val := `{` 
    for i, ing := range ings { 
     if i != 0 { 
      val += "," 
     } 
     val += fmt.Sprintf(`"('%s','%s','%s')"`, ing.Name, ing.Quantity, ing.Unit) 
    } 
    val += `}` 
    return val, nil 
} 

// e.g. `{"('flour','3','cups')"}` 

stmt := `INSERT INTO recipes (name, ingredients) VALUES ($1, $2::ingredient[])` 

// ... 
+0

완벽하게 작동했습니다. –

0

데이터베이스 디자인이 매우 복잡하고 SQL의 장점 (및 약점)을 고려하지 않은 것으로 보입니다.

레시피를 참조하여 재료를 각자의 테이블에 나눠 줄 것을 제안합니다. 그런 다음 전체 레서피를 찾는 작업은 JOIN입니다. 조리법을 삽입

CREATE TABLE ingredients (
    recipe_id uuid, 
    name text, 
    quantity int, 
    unit text 
); 

CREATE TABLE recipes (
    recipe_id uuid PRIMARY KEY, 
    name text 
); 

하고 그것을 읽을 조회 :

DB를 만들기

INSERT INTO recipes VALUES (
    '5d1cb631-37bd-46cc-a278-4c8558ed8964', 'cake1' 
); 

INSERT INTO ingredients (recipe_id, name, quantity, unit) VALUES 
    ('5d1cb631-37bd-46cc-a278-4c8558ed8964', 'flour', 3, 'cups'), 
    ('5d1cb631-37bd-46cc-a278-4c8558ed8964', 'water', 1, 'cups') 
; 

SELECT r.name, i.name, i.quantity, i.unit 
FROM ingredients AS i 
INNER JOIN recipes AS r 
ON r.recipe_id=i.recipe_id; 

SQLFiddle 링크 : http://sqlfiddle.com/#!17/262ad/14

+0

나는이 시점에서 그렇게 할 것을 고려하고있다. 개별 항목에 액세스하거나 개별 항목으로 검색 할 필요없이 단순한 배열 만 필요했기 때문에 이전에 없었던 이유가있었습니다. –