2017-05-21 1 views
1

postgresql에서 간단한 질문 태그 데이터베이스 연결을 개발 중입니다. 질문은 여러 개의 태그를 가질 수 있으며 하나의 태그는 여러 개의 질문과 연관 될 수 있습니다. 그래서 M-M 테이블, 질문 표가있는 것입니다. PHP 사용하기 질문을 만들고 주어진 태그가 이미 태그 테이블에 없는지 확인하고 싶습니다. 그들이 존재 할 경우테이블에 반복 데이터를 삽입하고 연결하십시오.

  • 을 태그 테이블에 내 태그를 추가하지 마십시오
  • 이 존재 나던 태그가 내가 원하는 경우 내 questiontags 테이블

의 질문에 미리 존재하지 않는 태그를 연결합니다 그것을 태그 테이블에 추가 한 다음 assoaciation을 작성하십시오. 이를 위해

나는 이런 식으로 뭔가를 시도했다 :

function update_tags($questionid, $tags) { 
    global $conn; 

    //Check if question already exists. If Yes, delete it from the array -> //EDIT PROPOSES 
    $questiontags = get_all_tags(); 
    $existant_tags = []; 

    foreach ($questiontags as $tag_idx => $tag) { 
     if(in_array($tag['name'], $tags)){ 
      $key = array_search($tag['name'], $tags); 
      unset($tags[$key]); 
      $existant_tags[] = $tag['tagid']; 
      associate_only_tag($tag['tagid'], $questionid); 

     } 
     $questiontags[$tag_idx] = $tag['tagid']; 
    } 

    foreach ($tags as $tag) { 
     associate_tag($tag, $questionid); 
    } 

    $tags_to_delete = array_diff($questiontags, $existant_tags); 

    foreach ($tags_to_delete as $tagid) { 
     delete_tag_from_question($tagid, $questionid); 
    } 
} 

function get_all_tags() { 

    global $conn; 

    $query=$conn->prepare("SELECT tags.tagid, tags.name FROM tags "); 
    $query->execute(); 

    return $query->fetchAll(); 

} 

function get_tags_from_question($questionid) { 
    global $conn; 

    $query=$conn->prepare("SELECT tags.tagid, tags.name FROM tags 
INNER JOIN questiontags 
ON tags.tagid = questiontags.tagid 
WHERE questiontags.questionid = :question 
"); 
    $query->execute(['question' => $questionid]); 

    return $query->fetchAll(); 
} 

function insert_tag($tag) 
{ 
    global $conn; 
    $stmt = $conn->prepare("INSERT INTO tags (name) VALUES(:tag)"); 
    $stmt->execute([$tag]); 
    return (int)$conn->lastInsertId(); 
} 

function associate_tag($tag, $questionid) 
{ 
    global $conn; 
    $tagid = insert_tag($tag); 
    $stmt = $conn->prepare("INSERT INTO questiontags (questionid, tagid) VALUES(:question, :tag)"); 
    $stmt->execute(['question' => $questionid, 'tag' => $tagid]); 
} 

function associate_only_tag($tagid, $questionid) 
{ 
    global $conn; 
    $stmt = $conn->prepare("INSERT INTO questiontags (questionid, tagid) VALUES(:question, :tag)"); 
    $stmt->execute(['question' => $questionid, 'tag' => $tagid]); 
} 

function delete_tag_from_question($tagid, $questionid) { 
    global $conn; 

    $query = $conn->prepare("DELETE FROM questiontags WHERE questionid = :question AND tagid = :tag"); 
    $query->execute(['question' => $questionid, 'tag' => $tagid]); 
} 

문제는이 단지 새로운 질문을위한 작품이 아니라이 때 질문을 업데이트 할 것입니다. 내가 할 때 associate_only_tag 질문이 존재하는지 확인하고 새로운 행을 새로 만들려고 시도 대신 업데이 트 뭔가가 필요 questiontags. 많은 노력 끝에 나는 그것을 이해할 수 없다.

이 작업을 수행 할 수있는 방법이 있습니까?

답변

0

CTE에서 과 INSERT ... ON CONFLICT DO NOTHING이라는 단일 쿼리를 제안합니다 (단, 맞습니다!).
그리고 당신은 편의를 위해 VARIADIC 입력 매개 변수를 사용하여 서버 측 SQL 함수를 사용하는 기능으로이 포장하려는 경우 : 이것은 배열 (또는 목록)에서 모든 태그를 생성

CREATE OR REPLACE FUNCTION update_tags(_questionid int, VARIADIC _tags text[]) 
    RETURNS void AS 
$func$ 
    WITH ins_tags AS (
     INSERT INTO tags (name) 
     SELECT * FROM unnest(_tags) 
     ON  CONFLICT (name) DO NOTHING 
     RETURNING tagid 
    ) 
    INSERT INTO questiontags (questionid, tagid) 
    SELECT _questionid, i.tagid FROM ins_tags i 
    UNION ALL 
    SELECT _questionid, t.tagid FROM tags t 
    WHERE t.name = ANY(_tags) 
    ON  CONFLICT (questionid, tagid) DO NOTHING; 
$func$ LANGUAGE sql; 

존재하지 않았다, 아직. 그리고 그것은 주어진 질문을 그들 모두와 연관시킵니다 - 이미 연관되어 있지 않다면.

name의 테이블은 tags이고 (questionid, tagid) 테이블의 테이블은 questiontags이어야합니다. 둘 다 일반적으로 many-to-many implementation에 있습니다. 그렇지 않으면 각각을 만듭니다.

그리고 tags.tagidserial 열로 가정합니다. 또한 사실이어야합니다.

전화 :

SELECT update_tags(123, 'foo', 'bar'); 

또는 :

SELECT update_tags(123, VARIADIC '{foo,bar}'::text[]); 

아직 동시 쓰기로드 (심지어 매우 않을 경우)에서 실패 할 수 있습니다. 그럴 경우 ON CONFLICT ... DO UPDATE을 대신 사용하십시오. 상세 설명 :

소개 VARIADIC :

관련 문제