Discourse AI + 데이터 탐색기?

엣지 케이스의 예를 몇 가지 공유해 주시겠어요?

1개의 좋아요

네, 다음 주에 한 번 더 진행할 계획이에요. 제가 공유한 저장소의 Ruby 스크립트를 사용해 보세요.

@simon 저는 진정한 가치는 적어도 가까운 미래에는 처음부터 모든 것을 올바르게 하는 일이 결코 없을 것이라는 점을 이해하는 데 있다고 생각합니다. 하지만 원하는 것이 무엇인지 알고 있다면, 지루한 일을 처리할 수 있는 끈질긴 인턴처럼 그 방향으로 이끌어갈 수 있습니다.

그래서, SQL에 대한 현재 지식이 select from where를 기억하는 것 외에는 없었고, 최종 결과물이 어떻게 되어야 하는지 알고 있었을 뿐인데, 제 일상 업무를 크게 방해하지 않으면서도 ChatGPT와 옆에서 대화를 나누는 것만으로 제가 원하는 쿼리를 만들어냈습니다. 정말로 제가 올바른 방향으로 계속 이끌기만 하면 되는 무료 개인 비서/인턴을 둔 것과 같습니다.

우선, 여기 제 최종 쿼리를 올립니다. 저는 총 좋아요 수가 가장 많은 상위 100명의 사용자를 반환하는 쿼리를 원했고, 그들의 user, username, 총 링크 수, 그리고 단일 좋아요 수가 가장 많았던 하나의 게시물에 대한 링크를 제공받기를 원했습니다. 저는 이걸 어떻게 가져와야 하는지 전혀 몰랐고(지금은 여전히 모릅니다), 솔직히 어디서부터 시작해야 할지조차 몰랐습니다. 이런 것을 원할 때 저는 보통 제 엔지니어 중 한 명을 귀찮게 합니다. 하지만 이번에는 그들을 귀찮게 하지 않으면서도 제 업무 속도를 늦추지 않고, ChatGPT를 지시하여 제가 필요한 작업을 완료할 수 있었습니다.

최종 쿼리:

WITH Most_Liked_Posts AS (
  SELECT
    p.user_id,
    p.topic_id,
    p.post_number,
    ROW_NUMBER() OVER (PARTITION BY p.user_id ORDER BY likes_count DESC) AS row_number
  FROM (
    SELECT
      p.user_id,
      p.topic_id,
      p.post_number,
      COUNT(l.id) AS likes_count
    FROM
      posts p
      LEFT JOIN post_actions l ON p.id = l.post_id AND l.post_action_type_id = 2
    WHERE
      p.user_id NOT IN (SELECT id FROM users WHERE username = 'codey')
    GROUP BY
      p.user_id,
      p.topic_id,
      p.post_number
  ) p
),
User_Likes AS (
  SELECT
    u.id AS user_id,
    COUNT(pa.id) AS total_likes
  FROM
    users u
    LEFT JOIN posts p ON u.id = p.user_id
    LEFT JOIN post_actions pa ON p.id = pa.post_id AND pa.post_action_type_id = 2
  WHERE
    u.username != 'codey'
  GROUP BY
    u.id
)
SELECT
  ul.user_id,
  u.username AS username,
  ul.total_likes,
  '<a href="/discuss/t/' || mlp.topic_id || '/' || mlp.post_number || '">' || 'Link to most-liked post' || '</a>' AS html$post
FROM
  User_Likes ul
  JOIN users u ON ul.user_id = u.id
  LEFT JOIN Most_Liked_Posts mlp ON ul.user_id = mlp.user_id AND mlp.row_number = 1
ORDER BY
  ul.total_likes DESC
LIMIT 100

프롬프트를 시작하기 전 제 ChatGPT 계정에서 설정한 지침:

저는 PostgreSQL과 관련된 질문만 할 것이며, 관련 SQL 쿼리로만 답변해 주십시오.

이 쿼리들은 모두 제 커뮤니티 플랫폼인 Discourse의 데이터베이스와 관련이 있습니다.
SQL 쿼리와 쿼리에 대한 설명으로 답변해 주십시오. SQL 문장을 끝내는 세미콜론은 필요하지 않으므로 사용하지 마십시오.

이 쿼리를 만들기 위해 ChatGPT와 나눈 전체 대화는 여기서 확인하세요:

3개의 좋아요

@sam, 위에서와 같이 완전한 스키마가 있다면(다시 한번 죄송하지만, 이 스키마를 실제로 어디서 구할 수 있는지, 혹은 이 형식으로 가져오는 방법을 잘 모르겠습니다) 그리고 langchain과 벡터 데이터베이스를 사용하여 문서를 ChatGPT로 보내기 이전에 올바르게 처리하고, Data Explorer 사용법에 대한 문서도 있다면… 이 프로세스가 거의 마법에 가까운 결과를 낼 것이라고 꽤 확신합니다.

벡터 DB를 시도해 보고 유사한 예시를 입력해 봤는데, 모델이 너무 강하게 유도됩니다. 마법 같은 결과를 얻으려면 매우 유사한 예시 1,000개 정도가 필요할 것 같습니다.

1개의 좋아요

우리 팀 엔지니어도 시도해 보겠습니다. 이런 것들을 꽤 빠르게 생성할 수 있는 일종의 "팩토리"를 구축해 두었거든요.

이 형식으로 모든 DB 스키마에 대한 완전하고 포괄적인 문서를 구할 수 있는 곳이 있을까요?

# == Schema Information
#
# Table name: application_requests
#
#  id       :integer          not null, primary key
#  date     :date             not null
#  req_type :integer          not null ("http_total"=>0,"http_2xx"=>1,"http_background"=>2,"http_3xx"=>3,"http_4xx"=>4,"http_5xx"=>5,"page_view_crawler"=>6,"page_view_logged_in"=>7,"page_view_anon"=>8,"page_view_logged_in_mobile"=>9,"page_view_anon_mobile"=>10,"api"=>11,"user_api"=>12)
#  count    :integer          default(0), not null
#
# Table name: users
#
#  id                        :integer          not null, primary key
#  username                  :string(60)       not null
#  created_at                :datetime         not null
#  updated_at                :datetime         not null
#  name                      :string           (the user's real name)
#  last_posted_at            :datetime
#  active                    :boolean          default(FALSE), not null
#  username_lower            :string(60)       not null
#  last_seen_at              :datetime
#  admin                     :boolean          default(FALSE), not null
#  trust_level               :integer          not null
#  approved                  :boolean          default(FALSE), not null
#  approved_by_id            :integer
#  approved_at               :datetime
#  previous_visit_at         :datetime
#  suspended_at              :datetime
#  suspended_till            :datetime
#  date_of_birth             :date
#  ip_address                :inet
#  moderator                 :boolean          default(FALSE)
#  title                     :string
#  locale                    :string(10)
#  primary_group_id          :integer
#  registration_ip_address   :inet
#  staged                    :boolean          default(FALSE), not null
#  first_seen_at             :datetime
#  silenced_till             :datetime
1개의 좋아요

그 형식은 토큰 관점에서 보면 상당히 비효율적이지만, 데이터 탐색기에서 pg 스키마 테이블을 쿼리하면 됩니다 :slight_smile:

2개의 좋아요

하하, 알겠어. 그에게 확인해 보라고 할게. 말했잖아, 이 문제를 더 깊이 파고들 사람은 내가 아니거든. 만약 내가 그랬다면, 이런 질문을 하고 있지 않았을 거야!

1개의 좋아요

무언가를 제대로 동작하게 만들고 싶긴 한데, 이건 정말 어려운 문제입니다.

3개의 좋아요

이것은 단지 아이디어일 뿐이며, 실제로 시도해 본 적이 없습니다.

제 생각에는 필요한 쿼리들이 역할에 따라 다음과 같이 분류될 수 있습니다.

  • модератор (moderator)
  • 관리자 (admin)
  • 개발자 (developer)

따라서 필요한 테이블들은 가장 작은 집합인 모더레이터가 필요한 테이블부터 시작하여 점점 더 큰 집합으로 그룹화될 수 있습니다.

이제, 모더레이터가 필요한 데이터의 상당수는 특정 컬럼이 필요한 테이블들의 공통 조인(joins)에 기반할 것입니다. 따라서 뷰가 적합합니다.

전체 스키마 대신 이러한 공통 뷰를 사용하면, 많은 프롬프트들이 전체 스키마가 아닌 뷰만 전달하면 되므로 LLM이 가능한 솔루션을 생성하는 것이 훨씬 쉬워지기를 바랍니다.

도움이 되길 바랍니다.


훨씬 더 어려운 쿼리의 경우, 그러한 쿼리가 필요할 만큼 충분한 지식을 가지고 있다면, 그 쿼리를 작성할 수 있을 만큼 충분한 지식을 가지고 있을 가능성이 높습니다.

1개의 좋아요

그 방법을 시도해 보았습니다. 결과는 제각각입니다. GPT-3.5에 Discourse 데이터베이스 스키마의 최소 주석 버전만 제공하여 SQL 처리 능력을 테스트하는 것은 흥미로운 실험이 될 것입니다. 이는 토큰 측면에서 비효율적이라는 점을 인지하고 있지만, 가독성은 좋습니다:

최소 스키마
# == Schema Information
#
# Table name: users
#
#  id                        :integer          not null, primary key
#  username                  :string(60)       not null
#  created_at                :datetime         not null
#
# Table name: groups
#
#  id                                 :integer          not null, primary key
#  name                               :string           not null
#  created_at                         :datetime         not null
#
# Table name: group_users
#
#  id                 :integer          not null, primary key
#  group_id           :integer          not null
#  user_id            :integer          not null
#
# Table name: posts
#
#  id                      :integer          not null, primary key
#  user_id                 :integer
#  topic_id                :integer          not null
#  deleted_at              :datetime         (애플리케이션은 게시물을 "소프트 삭제"합니다. 게시물이 삭제되면 `deleted_at` 속성이 :datetime으로 설정됩니다. 삭제된 게시물을 반환하도록 명시적으로 요청하지 않는 한, 게시물 관련 데이터를 조회하는 쿼리를 작성할 때 `deleted_at` 컬럼이 `NOT NULL`인지 확인하십시오.)
#
# Table name: topics
#
#  id                        :integer          not null, primary key
#  title                     :string           not null
#  category_id               :integer
#  created_at                :datetime         not null
#  user_id                   :integer          (토픽을 생성한 사용자의 id)
#  deleted_at                :datetime         (애플리케이션은 토픽을 "소프트 삭제"합니다. 토픽이 삭제되면 `deleted_at` 속성이 :datetime으로 설정됩니다. 삭제된 토픽을 반환하도록 명시적으로 요청하지 않는 한, 토픽 관련 데이터를 조회하는 쿼리를 작성할 때 `deleted_at` 컬럼이 `NOT NULL`인지 확인하십시오.)
#
# Table name: categories
#
#  id                                        :integer          not null, primary key
#  name                                      :string(50)       not null

지금까지 얻은 결과는 인상적입니다. 이는 프롬프트에 주석이 달린 스키마만 제공하면 문제를 해결할 수 있다는 생각으로 이어집니다.

재미로, GPT-3.5가 어떤 종류의 쿼리에서 어려움을 겪을지 예상하는지 물어보았습니다:

GPT-3.5의 한계
  1. 계층적 쿼리: 재귀적 관계와 같은 계층적 데이터 구조를 포함하는 쿼리는 복잡할 수 있습니다. 예를 들어, 카테고리 계층 구조 내의 부모-자식 관계를 조회하는 경우입니다.
  2. 시간 기반 쿼리: 과거 추세를 계산하거나 특정 시간 간격에 걸쳐 데이터를 분석하는 등 시간 기반 데이터를 포함하는 쿼리는 더 정교한 SQL 구조가 필요할 수 있습니다.
  3. 윈도우 함수: 이전 쿼리에서 윈도우 함수를 사용했지만, 복잡한 분석 작업을 위한 더 고급 윈도우 함수 사용은 어려울 수 있습니다.
  4. 지리 공간 쿼리: 공간 데이터베이스와 지리적 데이터 기반 쿼리(예: 근처 위치 찾기 또는 복잡한 공간 관계)는 복잡할 수 있습니다.
  5. 복잡한 집계: 여러 수준의 그룹화와 계산을 포함하는 고급 집체는 더 정교한 SQL 지식이 필요할 수 있습니다.
  6. 최적화 쿼리: 인덱스, 쿼리 튜닝 및 성능 향상을 포함하는 altamente 최적화된 쿼리를 작성하는 것은 복잡할 수 있습니다.
  7. 사용자 정의 알고리즘: SQL 쿼리 내에서 사용자 정의 알고리즘이나 로직을 구현하는 것은 포함된 로직의 복잡성 때문에 어려울 수 있습니다.
  8. 고급 JOIN: 복잡한 조인 조건 또는 여러 조인의 결합은 신중한 고려와 더 복잡한 SQL 구조가 필요할 수 있습니다.
  9. 서브쿼리와 상관 서브쿼리: 서브쿼리를 처리할 수 있지만, 극도로 복잡하거나 깊게 중첩된 서브쿼리는 더 어려울 수 있습니다.
  10. 피벗과 언피벗: 데이터를 넓은 형식과 긴 형식 간에 변환하는 것(피벗과 언피벗)은 특정 시나리오에서 복잡해질 수 있습니다.

제가 문제를 겪고 있는 부분은 전체 데이터베이스 스키마의 모호성을 제거하는 것입니다. 예를 들어, user_actions 테이블에 주석을 달기 위한 방법을 찾는 것 등이 있습니다. 단순히 action_type 코드의 정의만 제공하기에는 부족합니다. 모델은 user_id, target_user_id, acting_user_id에 대해 추측을 시작합니다.

가장 자주 요청되는 쿼리는 데이터베이스의 대부분의 테이블과 컬럼을 사용하지 않습니다. Data Explorer에 AI가 추가된다면, “기본” 모드와 “고급” 모드를 제공하는 것을 살펴볼 가치가 있을 수 있습니다. 기본 모드에서는 대부분의 사용 사례를 커버하는 프롬프트를 제공할 수 있습니다. 고급 모드에서는 사용자가 프롬프트에 설정할 정보를 선택할 수 있게 할 수 있습니다.

meta에서 쿼리에 대한 몇 가지 요청을 거꾸로 추적하여, GPT-3.5가 쿼리를 성공적으로 생성하기 위해 프롬프트에 무엇을 제공해야 하는지 살펴보는 것도 흥미로울 것입니다.

3개의 좋아요

먼저 GPT에게 관련 테이블을 식별하도록 한 뒤, 두 번째 단계에서 SQL을 생성하는 LangChain 방식이 도움이 될 수 있습니다.

3개의 좋아요

현재 저희 제품 구현에는 langchain을 사용하고 있습니다. 사실 재사용 가능한 팩토리를 조금 구축해 두었는데, 곧 시니어 엔지니어에게 이 부분을 테스트해 보도록 할 예정입니다.

말씀드린 대로, 지금까지의 결과에는 꽤 만족하고 있습니다. 마치 장을 봐 주거나 대신 일을 처리해 주는 비서가 있는 것과 같다고 할까요. 몇 번의 요청은 필요하지만, 현재로서는 시간과 비용을 크게 절약해 주고 있습니다.

2개의 좋아요

참고

2개의 좋아요

이건 정말 중독성이 강합니다. “LLMs and SQL” 블로그 글과 몇 번의 시행착오를 바탕으로, Discourse 데이터베이스의 일부 설명을 담은 이 프롬프트를 만들었습니다:

Discourse 데이터베이스 프롬프트
/* Discourse database documentation start */와 /* Discourse database documentation end */ 주석 사이의 텍스트에는 Discourse 포럼 애플리케이션의 PostgreSQL 데이터베이스에 대한 세부 정보가 포함되어 있습니다.
모든 테이블과 컬럼은 `CREATE TABLE` 문에 정의되어 있습니다. 각 `CREATE TABLE` 문 뒤에 이어지는 샘플 쿼리에 유의하세요. 몇 가지 추가적인 중요한 세부 사항은 인라인(`-- --`) 및 멀티라인(`/* */`) 주석에 포함되어 있습니다. 이 정보를 전송한 후, Discourse Data Explorer 플러그인으로 실행될 쿼리를 작성해 달라고 요청할 것입니다. 이 쿼리를 작성하는 데 필요한 모든 테이블과 컬럼은 제가 전송해 드린 `CREATE TABLE` 문에 포함되어 있습니다.

/* Discourse database documentation start */

CREATE TABLE users (
    id integer NOT NULL, -- 애플리케이션에는 '실제' 사용자의 개념이 있습니다. '실제' 사용자는 id > 0인 사용자입니다. --
    username character varying(60) NOT NULL,
    created_at timestamp without time zone NOT NULL,
    updated_at timestamp without time zone NOT NULL,
    name character varying,
    seen_notification_id integer DEFAULT 0 NOT NULL,
    last_posted_at timestamp without time zone,
    password_hash character varying(64),
    salt character varying(32),
    active boolean DEFAULT false NOT NULL,
    username_lower character varying(60) NOT NULL,
    last_seen_at timestamp without time zone,
    admin boolean DEFAULT false NOT NULL,
    last_emailed_at timestamp without time zone,
    trust_level integer NOT NULL,
    approved boolean DEFAULT false NOT NULL,
    approved_by_id integer,
    approved_at timestamp without time zone,
    previous_visit_at timestamp without time zone,
    suspended_at timestamp without time zone,
    suspended_till timestamp without time zone,
    date_of_birth date,
    views integer DEFAULT 0 NOT NULL,
    flag_level integer DEFAULT 0 NOT NULL,
    ip_address inet,
    moderator boolean DEFAULT false,
    title character varying,
    uploaded_avatar_id integer,
    locale character varying(10),
    primary_group_id integer,
    registration_ip_address inet,
    staged boolean DEFAULT false NOT NULL,
    first_seen_at timestamp without time zone,
    silenced_till timestamp without time zone,
    group_locked_trust_level integer,
    manual_locked_trust_level integer,
    secure_identifier character varying,
    flair_group_id integer,
    last_seen_reviewable_id integer
);
SELECT * FROM users WHERE id = 1 OR id = 2 OR id = 121;
id  | username |         created_at         |         updated_at         |     name     | seen_notification_id |       last_posted_at       |                          password_hash                           |               salt               | active | username_lower |        last_seen_at        | admin |      last_emailed_at       | trust_level | approved | approved_by_id |        approved_at         |     previous_visit_at      |        suspended_at        |     suspended_till      | date_of_birth | views | flag_level | ip_address | moderator |   title    | uploaded_avatar_id | locale | primary_group_id | registration_ip_address | staged |       first_seen_at        | silenced_till | group_locked_trust_level | manual_locked_trust_level |            secure_identifier             | flair_group_id | last_seen_reviewable_id |      password_algorithm
-----+----------+----------------------------+----------------------------+--------------+----------------------+----------------------------+------------------------------------------------------------------+----------------------------------+--------+----------------+----------------------------+-------+----------------------------+-------------+----------+----------------+----------------------------+----------------------------+----------------------------+-------------------------+---------------+-------+------------+------------+-----------+------------+--------------------+--------+------------------+-------------------------+--------+----------------------------+---------------+--------------------------+---------------------------+------------------------------------------+----------------+-------------------------+------------------------------
   1 | scossar  | 2019-04-26 22:59:44.685893 | 2023-08-14 04:40:20.823438 | Simon Cossar |                56395 | 2023-08-14 04:08:43.430717 | 9547d42a1dc5759a0c22ed2c97c490dac845ed76ebc4a412f885ceb908965794 | 304898f78b8b732b1d64011c0d086e91 | t      | scossar        | 2023-08-14 04:40:56.769353 | t     | 2023-08-14 04:33:46.44485  |           3 | t        |             -1 | 2020-09-22 19:54:41.05418  | 2023-08-13 22:35:00.020816 |                            |                         | 1904-02-14    |     0 |          0 | ::1        | t         | Member     |                747 |        |                  |                         | f      | 2019-04-26 23:10:43.250255 |               |                          |                         3 |                                          |                |                     432 | $pbkdf2-sha256$i=64000,l=32$
   2 | sally    | 2019-04-26 23:15:47.859691 | 2023-08-14 04:40:56.831344 |              |                56396 | 2023-08-14 04:11:37.417456 | e1f0be57f784827602613c35ebd4b4087f858c715ebea1b3027f8c520bffbdf9 | ff59f100b4bdd43524f94e3a2f808106 | t      | sally          | 2023-08-14 04:33:57.054779 | f     | 2023-08-14 04:33:06.727322 |           2 | t        |             -1 | 2020-05-19 19:35:15.79381  | 2023-08-13 22:08:05.099486 |                            |                         |               |     0 |          0 | 127.0.0.1  | t         | Regular    |                 22 | en     |               49 | 127.0.0.1               | f      | 2019-04-26 23:16:58.912958 |               |                          |                           | a292161dd2ebbedbcd0e79f96baca06d9f399083 |            194 |                     432 | $pbkdf2-sha256$i=64000,l=32$
 121 | Ben      | 2019-11-15 16:31:38.216013 | 2023-08-14 04:40:41.907605 |              |                56314 | 2023-07-07 20:48:33.496471 | 364180ae133b9b8bb560d30a41b9854f96e069ef2f7f95d957d2dc7122752074 | b6b40fd3b4e2e1236cef027c222f73bc | t      | ben            | 2023-08-14 04:39:47.42192  | f     | 2023-08-14 04:30:26.764459 |           2 | t        |              1 | 2019-11-15 16:31:38.089553 | 2023-07-22 02:14:01.540478 | 2022-05-05 17:39:02.632952 | 2022-05-06 17:38:55.054 |               |     0 |          0 | 127.0.0.1  | f         | Prime Four |                    | en     |              196 | 127.0.0.1               | f      | 2019-11-15 16:31:38.714185 |               |                          |                           |                                          |            196 |                         | $pbkdf2-sha256$i=64000,l=32$

CREATE TABLE groups (
    id integer NOT NULL,
    name character varying NOT NULL,
    created_at timestamp without time zone NOT NULL,
    updated_at timestamp without time zone NOT NULL,
    automatic boolean DEFAULT false NOT NULL,
    user_count integer DEFAULT 0 NOT NULL,
    automatic_membership_email_domains text,
    primary_group boolean DEFAULT false NOT NULL,
    title character varying,
    grant_trust_level integer,
    incoming_email character varying,
    has_messages boolean DEFAULT false NOT NULL,
    flair_url character varying,
    flair_bg_color character varying,
    flair_color character varying,
    bio_raw text,
    bio_cooked text,
    allow_membership_requests boolean DEFAULT false NOT NULL,
    full_name character varying,
    default_notification_level integer DEFAULT 3 NOT NULL,
    visibility_level integer DEFAULT 0 NOT NULL,
    public_exit boolean DEFAULT false NOT NULL,
    public_admission boolean DEFAULT false NOT NULL,
    membership_request_template text,
    messageable_level integer DEFAULT 0,
    mentionable_level integer DEFAULT 0,
    members_visibility_level integer DEFAULT 0 NOT NULL,
    publish_read_state boolean DEFAULT false NOT NULL,
    flair_icon character varying,
    flair_upload_id integer,
    smtp_server character varying,
    smtp_port integer,
    smtp_ssl boolean,
    imap_server character varying,
    imap_port integer,
    imap_ssl boolean,
    imap_mailbox_name character varying DEFAULT ''::character varying NOT NULL,
    imap_uid_validity integer DEFAULT 0 NOT NULL,
    imap_last_uid integer DEFAULT 0 NOT NULL,
    email_username character varying,
    email_password character varying,
    imap_last_error text,
    imap_old_emails integer,
    imap_new_emails integer,
    allow_unknown_sender_topic_replies boolean DEFAULT false NOT NULL,
    smtp_enabled boolean DEFAULT false,
    smtp_updated_at timestamp without time zone,
    smtp_updated_by_id integer,
    imap_enabled boolean DEFAULT false,
    imap_updated_at timestamp without time zone,
    imap_updated_by_id integer,
    assignable_level integer DEFAULT 0 NOT NULL,
    email_from_alias character varying
); -- 'admin' 또는 'moderator' 상태인 사용자는 자동 "staff" 그룹에 추가됩니다. --

SELECT * FROM groups WHERE id = 1 OR id = 11 OR id = 49;
id |     name      |         created_at         |         updated_at         | automatic | user_count | automatic_membership_email_domains | primary_group |   title    | grant_trust_level | incoming_email | has_messages | flair_bg_color | flair_color |      bio_raw       |        bio_cooked         | allow_membership_requests |         full_name         | default_notification_level | visibility_level | public_exit | public_admission | membership_request_template | messageable_level | mentionable_level | members_visibility_level | publish_read_state | flair_icon | flair_upload_id | smtp_server | smtp_port | smtp_ssl | imap_server | imap_port | imap_ssl | imap_mailbox_name | imap_uid_validity | imap_last_uid | email_username | email_password | imap_last_error | imap_old_emails | imap_new_emails | allow_unknown_sender_topic_replies | smtp_enabled |      smtp_updated_at      | smtp_updated_by_id | imap_enabled | imap_updated_at | imap_updated_by_id | assignable_level | email_from_alias
----+---------------+----------------------------+----------------------------+-----------+------------+------------------------------------+---------------+------------+-------------------+----------------+--------------+----------------+-------------+--------------------+---------------------------+---------------------------+---------------------------+----------------------------+------------------+-------------+------------------+-----------------------------+-------------------+-------------------+--------------------------+--------------------+------------+-----------------+-------------+-----------+----------+-------------+-----------+----------+-------------------+-------------------+---------------+----------------+----------------+-----------------+-----------------+-----------------+------------------------------------+--------------+---------------------------+--------------------+--------------+-----------------+--------------------+------------------+------------------
  1 | admins        | 2019-04-26 22:58:35.997964 | 2021-08-05 19:11:22.699825 | t         |          1 |                                    | f             |            |                   |                | t            |                |             |                    |                           | f                         |                           |                          3 |                1 | f           | f                |                             |                99 |                 0 |                        0 | f                  |            |                 |             |           |          |             |           |          |                   |                 0 |             0 |                |                |                 |                 |                 | f                                  | f            |                           |                    | f            |                 |                    |                0 |
 11 | trust_level_1 | 2019-04-26 22:58:36.033238 | 2021-10-05 19:54:51.043121 | t         |        116 |                                    | f             |            |                   |                | t            |                |             |                    |                           | f                         |                           |                          3 |                1 | f           | f                |                             |                 0 |                 0 |                        0 | f                  |            |                 |             |           |          |             |           |          |                   |                 0 |             0 |                |                |                 |                 |                 | f                                  | f            |                           |                    | f            |                 |                    |                0 |
 49 | eurorack      | 2019-10-03 17:28:42.323203 | 2022-08-16 19:54:09.223307 | f         |         84 | example.com                        | t             | Euroracker |                 3 |                | t            |                |             | All about eurorack+| <p>All about eurorack</p> | f                         | Eurorack Enthusiasts Club |                          3 |                0 | f           | t                | Can I join this group?      |                99 |                99 |                        0 | t                  |            |                 |             |           |          |             |           |          |                   |                 0 |             0 |                |                |                 |                 |                 | f                                  | f            | 2022-02-11 23:24:28.76631 |                  1 | f            |                 |                    |                0 |

/* group_users 테이블은 groups와 users 테이블을 연결합니다. */
CREATE TABLE group_users (
    id integer NOT NULL,
    group_id integer NOT NULL,
    user_id integer NOT NULL,
    created_at timestamp without time zone NOT NULL,
    updated_at timestamp without time zone NOT NULL,
    owner boolean DEFAULT false NOT NULL,
    notification_level integer DEFAULT 2 NOT NULL,
    first_unread_pm_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);
SELECT * FROM group_users WHERE id = 13 OR id = 8219 OR id = 9137;
  id  | group_id | user_id |         created_at         |         updated_at         | owner | notification_level |     first_unread_pm_at
------+----------+---------+----------------------------+----------------------------+-------+--------------------+----------------------------
   13 |        3 |       1 | 2019-04-26 22:59:47.828533 | 2019-04-26 22:59:47.828533 | f     |                  2 | 2023-08-14 01:14:54.229593
 8219 |       13 |     121 | 2022-04-21 08:15:47.946036 | 2022-04-21 08:15:47.946036 | f     |                  2 | 2023-07-05 06:49:04.48265
 9137 |       49 |       2 | 2022-09-08 17:34:39.290504 | 2022-09-08 17:34:39.290504 | t     |                  3 | 2020-11-21 02:40:15.868728

CREATE TABLE posts (
    id integer NOT NULL,
    user_id integer,
    topic_id integer NOT NULL,
    post_number integer NOT NULL,
    raw text NOT NULL,
    cooked text NOT NULL,
    created_at timestamp without time zone NOT NULL,
    updated_at timestamp without time zone NOT NULL,
    reply_to_post_number integer,
    reply_count integer DEFAULT 0 NOT NULL,
    quote_count integer DEFAULT 0 NOT NULL,
    deleted_at timestamp without time zone, -- 애플리케이션은 게시글과 주제를 "soft delete"만 수행합니다. 삭제된 게시글이나 주제에 대한 세부 정보를 반환해 달라고 명시적으로 요청하지 않는 한, 게시글이나 주제와 관련된 쿼리를 작성할 때는 항상 `deleted_at IS NULL`인지 확인하세요. --
    off_topic_count integer DEFAULT 0 NOT NULL,
    like_count integer DEFAULT 0 NOT NULL,
    incoming_link_count integer DEFAULT 0 NOT NULL,
    bookmark_count integer DEFAULT 0 NOT NULL,
    score double precision,
    reads integer DEFAULT 0 NOT NULL,
    post_type integer DEFAULT 1 NOT NULL, -- :regular=>1, :moderator_action=>2, :small_action=>3, :whisper=>4 --
    sort_order integer,
    last_editor_id integer,
    hidden boolean DEFAULT false NOT NULL,
    hidden_reason_id integer,
    notify_moderators_count integer DEFAULT 0 NOT NULL,
    spam_count integer DEFAULT 0 NOT NULL,
    illegal_count integer DEFAULT 0 NOT NULL,
    inappropriate_count integer DEFAULT 0 NOT NULL,
    last_version_at timestamp without time zone NOT NULL,
    user_deleted boolean DEFAULT false NOT NULL,
    reply_to_user_id integer,
    percent_rank double precision DEFAULT 1.0,
    notify_user_count integer DEFAULT 0 NOT NULL,
    like_score integer DEFAULT 0 NOT NULL,
    deleted_by_id integer,
    edit_reason character varying,
    word_count integer,
    version integer DEFAULT 1 NOT NULL,
    cook_method integer DEFAULT 1 NOT NULL,
    wiki boolean DEFAULT false NOT NULL,
    baked_at timestamp without time zone,
    baked_version integer,
    hidden_at timestamp without time zone,
    self_edits integer DEFAULT 0 NOT NULL,
    reply_quoted boolean DEFAULT false NOT NULL,
    via_email boolean DEFAULT false NOT NULL,
    raw_email text,
    public_version integer DEFAULT 1 NOT NULL,
    action_code character varying,
    locked_by_id integer,
    image_upload_id bigint
);
SELECT * FROM posts WHERE id = 11094 OR id = 11095 OR id = 11096;
  id   | user_id | topic_id | post_number |                     raw                      |                                                                                                  cooked                                                                                                   |         created_at         |         updated_at         | reply_to_post_number | reply_count | quote_count | deleted_at | off_topic_count | like_count | incoming_link_count | bookmark_count | score | reads | post_type | sort_order | last_editor_id | hidden | hidden_reason_id | notify_moderators_count | spam_count | illegal_count | inappropriate_count |      last_version_at       | user_deleted | reply_to_user_id |   percent_rank    | notify_user_count | like_score | deleted_by_id | edit_reason | word_count | version | cook_method | wiki |          baked_at          | baked_version | hidden_at | self_edits | reply_quoted | via_email | raw_email | public_version | action_code | locked_by_id | image_upload_id |      outbound_message_id
-------+---------+----------+-------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------+----------------------------+----------------------+-------------+-------------+------------+-----------------+------------+---------------------+----------------+-------+-------+-----------+------------+----------------+--------+------------------+-------------------------+------------+---------------+---------------------+----------------------------+--------------+------------------+-------------------+-------------------+------------+---------------+-------------+------------+---------+-------------+------+----------------------------+---------------+-----------+------------+--------------+-----------+-----------+----------------+-------------+--------------+-----------------+--------------------------------
 11094 |       1 |     1852 |           7 | This is an example of a Discourse post.      | <p>This is an example of a Discourse post.</p>                                                                                                                                                            | 2023-08-14 04:08:43.430717 | 2023-08-14 04:08:43.430717 |                      |           0 |           0 |            |               0 |          0 |                   0 |              0 |   0.2 |     1 |         1 |          7 |              1 | f      |                  |                       0 |          0 |             0 |                   0 | 2023-08-14 04:08:43.441371 | f            |                  | 0.166666666666667 |                 0 |          0 |               |             |          8 |       1 |           1 | f    | 2023-08-14 04:08:43.430685 |             2 |           |          0 | f            | f         |           |              1 |             |              |                 |
 11095 |       2 |    10863 |          11 | This is another example of a Discourse post. | <p>This is another example of a Discourse post.</p>                                                                                                                                                       | 2023-08-14 04:11:37.417456 | 2023-08-14 04:11:37.417456 |                    5 |           0 |           0 |            |               0 |          0 |                   0 |              0 |   0.4 |     2 |         1 |         11 |              2 | f      |                  |                       0 |          0 |             0 |                   0 | 2023-08-14 04:11:37.430459 | f            |              121 |               0.8 |                 0 |          0 |               |             |          8 |       1 |           1 | f    | 2023-08-14 04:11:37.417417 |             2 |           |          0 | f            | f         |           |              1 |             |              |                 | discourse/post/11095@127.0.0.1
 11096 |     121 |    11047 |           2 | Thanks! I hadn't seen that :slight_smile:    | <p>Thanks! I hadn’t seen that <img src="//127.0.0.1:4200/images/emoji/twitter/slight_smile.png?v=12" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p> | 2023-08-14 04:13:09.346146 | 2023-08-14 05:17:10.659726 |                      |           0 |           0 |            |               0 |          0 |                   0 |              0 |   0.4 |     2 |         1 |          2 |              1 | f      |                  |                       0 |          0 |             0 |                   0 | 2023-08-14 05:17:10.589855 | f            |                  |                 0 |                 0 |          0 |               |             |          7 |       2 |           1 | f    | 2023-08-14 05:17:10.659607 |             2 |           |          0 | f            | f         |           |              2 |             |              |                 | discourse/post/11096@127.0.0.1

CREATE TABLE topics (
    id integer NOT NULL,
    title character varying NOT NULL,
    last_posted_at timestamp without time zone,
    created_at timestamp without time zone NOT NULL,
    updated_at timestamp without time zone NOT NULL,
    views integer DEFAULT 0 NOT NULL,
    posts_count integer DEFAULT 0 NOT NULL,
    user_id integer, -- 주제 생성자의 사용자 ID
    last_post_user_id integer NOT NULL,
    reply_count integer DEFAULT 0 NOT NULL,
    featured_user1_id integer,
    featured_user2_id integer,
    featured_user3_id integer,
    deleted_at timestamp without time zone, -- 애플리케이션은 게시글과 주제를 "연삭(soft delete)"만 수행합니다. 삭제된 게시글이나 주제에 대한 세부 정보를 반환하라고 명시적으로 요청하지 않는 한, 게시글 또는 주제와 관련된 쿼리를 작성할 때 항상 `deleted_at IS NULL`인지 확인해야 합니다. --
    highest_post_number integer DEFAULT 0 NOT NULL,
    like_count integer DEFAULT 0 NOT NULL,
    incoming_link_count integer DEFAULT 0 NOT NULL,
    category_id integer,
    visible boolean DEFAULT true NOT NULL,
    moderator_posts_count integer DEFAULT 0 NOT NULL,
    closed boolean DEFAULT false NOT NULL,
    archived boolean DEFAULT false NOT NULL,
    bumped_at timestamp without time zone NOT NULL,
    has_summary boolean DEFAULT false NOT NULL,
    archetype character varying DEFAULT 'regular'::character varying NOT NULL, -- archetype는 'regular' 또는 'private_message' 중 하나로 설정할 수 있습니다 --
    featured_user4_id integer,
    notify_moderators_count integer DEFAULT 0 NOT NULL,
    spam_count integer DEFAULT 0 NOT NULL,
    pinned_at timestamp without time zone,
    score double precision,
    percent_rank double precision DEFAULT 1.0 NOT NULL,
    subtype character varying,
    slug character varying,
    deleted_by_id integer,
    participant_count integer DEFAULT 1,
    word_count integer,
    excerpt character varying,
    pinned_globally boolean DEFAULT false NOT NULL,
    pinned_until timestamp without time zone,
    fancy_title character varying,
    highest_staff_post_number integer DEFAULT 0 NOT NULL,
    featured_link character varying,
    reviewable_score double precision DEFAULT 0.0 NOT NULL,
    image_upload_id bigint,
    slow_mode_seconds integer DEFAULT 0 NOT NULL,
    bannered_until timestamp without time zone,
    external_id character varying,
    CONSTRAINT has_category_id CHECK (((category_id IS NOT NULL) OR ((archetype)::text <> 'regular'::text))),
    CONSTRAINT pm_has_no_category CHECK (((category_id IS NULL) OR ((archetype)::text <> 'private_message'::text)))
);
SELECT * FROM topics WHERE id = 1852 OR id = 10863 OR id = 11047;
  id   |                title                |       last_posted_at       |         created_at         |         updated_at         | views | posts_count | user_id | last_post_user_id | reply_count | featured_user1_id | featured_user2_id | featured_user3_id | deleted_at | highest_post_number | like_count | incoming_link_count | category_id | visible | moderator_posts_count | closed | archived |         bumped_at          | has_summary |    archetype    | featured_user4_id | notify_moderators_count | spam_count | pinned_at |       score       | percent_rank |   subtype    |                slug                 | deleted_by_id | participant_count | word_count |                                      excerpt                                      | pinned_globally | pinned_until |             fancy_title             | highest_staff_post_number | featured_link | reviewable_score | image_upload_id | slow_mode_seconds | bannered_until | external_id
-------+-------------------------------------+----------------------------+----------------------------+----------------------------+-------+-------------+---------+-------------------+-------------+-------------------+-------------------+-------------------+------------+---------------------+------------+---------------------+-------------+---------+-----------------------+--------+----------+----------------------------+-------------+-----------------+-------------------+-------------------------+------------+-----------+-------------------+--------------+--------------+-------------------------------------+---------------+-------------------+------------+-----------------------------------------------------------------------------------+-----------------+--------------+-------------------------------------+---------------------------+---------------+------------------+-----------------+-------------------+----------------+-------------
  1852 | Ask Me Anything                     | 2023-08-14 04:08:43.430717 | 2020-05-04 22:11:50.813596 | 2023-08-14 05:25:00.249902 |     2 |           3 |       1 |                 1 |           0 |                   |                   |                   |            |                   7 |          0 |                   2 |           3 | t       |                     4 | f      | f        | 2023-08-14 04:08:43.430717 | f           | regular         |                   |                       0 |          0 |           | 0.914285714285714 |            1 |              | ask-me-anything                     |               |                 1 |        157 | The AMA with @sally will be held on May 8th. Start sending in your questions now. | f               |              | Ask Me Anything                     |                         7 |               |                0 |                 |                 0 |                |
 10863 | Post the last picture on your phone | 2023-08-14 04:11:37.417456 | 2022-01-25 21:42:52.514124 | 2023-08-14 05:22:30.373683 |    18 |           5 |       2 |                 2 |           3 |               121 |                 1 |                   |            |                  11 |          4 |                   7 |          14 | t       |                     0 | f      | f        | 2023-08-14 04:11:37.417456 | f           | regular         |                   |                       0 |          0 |           |  6.54545454545455 |            1 |              | post-the-last-picture-on-your-phone |               |                 3 |        150 | This is a test. This should go to the review queue                                | f               |              | Post the last picture on your phone |                        11 |               |            70.75 |             445 |                 0 |                |
 11047 | Have you seen this post?            | 2023-08-14 04:13:09.346146 | 2022-05-12 22:26:39.280698 | 2023-08-14 05:16:41.106722 |     3 |           2 |       1 |               121 |           0 |                   |                   |                   |            |                   2 |          0 |                   0 |             | t       |                     0 | f      | f        | 2023-08-14 05:17:10.695009 | f           | private_message |                   |                       0 |          0 |           |               0.4 |            1 | user_to_user | have-you-seen-this-post             |               |                 2 |         11 | Here is another one…                                                              | f               |              | Have you seen this post?            |                         2 |               |                0 |                 |                 0 |                |


CREATE TABLE categories (
    id integer NOT NULL,
    name character varying(50) NOT NULL,
    color character varying(6) DEFAULT '0088CC'::character varying NOT NULL,
    topic_id integer,
    topic_count integer DEFAULT 0 NOT NULL,
    created_at timestamp without time zone NOT NULL,
    updated_at timestamp without time zone NOT NULL,
    user_id integer NOT NULL,
    topics_year integer DEFAULT 0,
    topics_month integer DEFAULT 0,
    topics_week integer DEFAULT 0,
    slug character varying NOT NULL,
    description text,
    text_color character varying(6) DEFAULT 'FFFFFF'::character varying NOT NULL,
    read_restricted boolean DEFAULT false NOT NULL,
    auto_close_hours double precision,
    post_count integer DEFAULT 0 NOT NULL,
    latest_post_id integer,
    latest_topic_id integer,
    "position" integer,
    parent_category_id integer,
    posts_year integer DEFAULT 0,
    posts_month integer DEFAULT 0,
    posts_week integer DEFAULT 0,
    email_in character varying,
    email_in_allow_strangers boolean DEFAULT false,
    topics_day integer DEFAULT 0,
    posts_day integer DEFAULT 0,
    allow_badges boolean DEFAULT true NOT NULL,
    name_lower character varying(50) NOT NULL,
    auto_close_based_on_last_post boolean DEFAULT false,
    topic_template text,
    contains_messages boolean,
    sort_order character varying,
    sort_ascending boolean,
    uploaded_logo_id integer,
    uploaded_background_id integer,
    topic_featured_link_allowed boolean DEFAULT true,
    all_topics_wiki boolean DEFAULT false NOT NULL,
    show_subcategory_list boolean DEFAULT false,
    num_featured_topics integer DEFAULT 3,
    default_view character varying(50),
    subcategory_list_style character varying(50) DEFAULT 'rows_with_featured_topics'::character varying,
    default_top_period character varying(20) DEFAULT 'all'::character varying,
    mailinglist_mirror boolean DEFAULT false NOT NULL,
    minimum_required_tags integer DEFAULT 0 NOT NULL,
    navigate_to_first_post_after_read boolean DEFAULT false NOT NULL,
    search_priority integer DEFAULT 0,
    allow_global_tags boolean DEFAULT false NOT NULL,
    reviewable_by_group_id integer,
    read_only_banner character varying,
    default_list_filter character varying(20) DEFAULT 'all'::character varying,
    allow_unlimited_owner_edits_on_first_post boolean DEFAULT false NOT NULL,
    default_slow_mode_seconds integer
);
SELECT * FROM categories WHERE id = 3 OR id = 16 OR id = 42;
 id |       name       | color  | topic_id | topic_count |         created_at         |         updated_at         | user_id | topics_year | topics_month | topics_week |       slug       |                                        description                                        | text_color | read_restricted | auto_close_hours | post_count | latest_post_id | latest_topic_id | position | parent_category_id | posts_year | posts_month | posts_week | email_in | email_in_allow_strangers | topics_day | posts_day | allow_badges |    name_lower    | auto_close_based_on_last_post | topic_template | contains_messages | sort_order | sort_ascending | uploaded_logo_id | uploaded_background_id | topic_featured_link_allowed | all_topics_wiki | show_subcategory_list | num_featured_topics | default_view |  subcategory_list_style   | default_top_period | mailinglist_mirror | minimum_required_tags | navigate_to_first_post_after_read | search_priority | allow_global_tags | reviewable_by_group_id | read_only_banner | default_list_filter | allow_unlimited_owner_edits_on_first_post | default_slow_mode_seconds | uploaded_logo_dark_id
----+------------------+--------+----------+-------------+----------------------------+----------------------------+---------+-------------+--------------+-------------+------------------+-------------------------------------------------------------------------------------------+------------+-----------------+------------------+------------+----------------+-----------------+----------+--------------------+------------+-------------+------------+----------+--------------------------+------------+-----------+--------------+------------------+-------------------------------+----------------+-------------------+------------+----------------+------------------+------------------------+-----------------------------+-----------------+-----------------------+---------------------+--------------+---------------------------+--------------------+--------------------+-----------------------+-----------------------------------+-----------------+-------------------+------------------------+------------------+---------------------+-------------------------------------------+---------------------------+-----------------------
  3 | Staff            | E45735 |        2 |          16 | 2019-04-26 22:58:38.813759 | 2023-02-16 08:35:51.424024 |      -1 |           0 |            0 |           0 | staff            | Private category for staff discussions. Topics are only visible to admins and moderators. | FFFFFF     | t               |                  |         23 |          11094 |           11250 |       41 |                    |          1 |           0 |          0 |          | f                        |          0 |         0 | t            | staff            | f                             |                |                   |            |                |                  |                        | t                           | f               | f                     |                   3 |              | rows_with_featured_topics | all                | f                  |                     0 | f                                 |               0 | f                 |                        |                  | all                 | f                                         |                           |
 16 | customer support | F7941D |      277 |          13 | 2019-07-17 20:18:13.584715 | 2023-08-11 20:50:35.548498 |       1 |           1 |            0 |           0 | customer-support | This description will appear on the categories page.                                      | FFFFFF     | f               |              720 |         68 |          11087 |           11236 |        0 |                    |         10 |           7 |          0 |          | f                        |          0 |         0 | f            | customer support | f                             |                |                   |            |                |              689 |                        | t                           | f               | f                     |                   4 |              | rows_with_featured_topics | all                | f                  |                     0 | f                                 |               0 | f                 |                        |                  | all                 | f                                         |                           |
 42 | eurorack         | 0088CC |      584 |          30 | 2019-10-03 17:29:33.782372 | 2023-08-13 21:35:09.514998 |       1 |           1 |            0 |           0 | eurorack         | All about Eurorack synths.                                                                | FFFFFF     | f               |                  |        142 |          11091 |           11183 |        1 |                    |         15 |           3 |          3 |          | f                        |          0 |         3 | t            | eurorack         | f                             |                |                   | posts      |                |                  |                        | t                           | f               | f                     |                   4 | latest       | rows_with_featured_topics | all                | f                  |                     2 | t                                 |               0 | f                 |                        |                  | all                 | f                                         |                           |

CREATE TABLE tags (
    id integer NOT NULL,
    name character varying NOT NULL,
    topic_count integer DEFAULT 0 NOT NULL,
    created_at timestamp without time zone NOT NULL,
    updated_at timestamp without time zone NOT NULL,
    pm_topic_count integer DEFAULT 0 NOT NULL,
    target_tag_id integer,
    description character varying
);
SELECT * FROM tags WHERE id = 183 OR id = 184 OR id = 185;
 id  |    name     |         created_at         |         updated_at         | pm_topic_count | target_tag_id | description | public_topic_count | staff_topic_count
-----+-------------+----------------------------+----------------------------+----------------+---------------+-------------+--------------------+-------------------
 183 | photos      | 2023-08-14 05:22:30.326838 | 2023-08-14 05:22:30.326838 |              0 |               |             |                  1 |                 1
 184 | meetup      | 2023-08-14 05:25:00.227547 | 2023-08-14 05:25:00.227547 |              0 |               |             |                  0 |                 1
 185 | icebreakers | 2023-08-14 06:18:39.214459 | 2023-08-14 06:18:39.214459 |              0 |               |             |                  1 |                 1

CREATE TABLE topic_tags (
    id integer NOT NULL,
    topic_id integer NOT NULL,
    tag_id integer NOT NULL,
    created_at timestamp without time zone NOT NULL,
    updated_at timestamp without time zone NOT NULL
);
SELECT * FROM topic_tags WHERE id = 1005 OR id = 1006 OR id = 1007;
  id  | topic_id | tag_id |         created_at         |         updated_at
------+----------+--------+----------------------------+----------------------------
 1005 |    10863 |    183 | 2023-08-14 05:22:30.360668 | 2023-08-14 05:22:30.360668
 1006 |     1852 |    184 | 2023-08-14 05:25:00.237298 | 2023-08-14 05:25:00.237298
 1007 |    10863 |    185 | 2023-08-14 06:18:39.240753 | 2023-08-14 06:18:39.240753

CREATE TABLE user_actions (
    id integer NOT NULL,
    action_type integer NOT NULL, -- :like=>1 (사용자가 게시글에 좋아요를 누를 때), :was_liked=>2 (사용자의 게시글에 좋아요가 붙을 때), :new_topic=>4, :reply=>5, :response=>6, :mention=>7, :quote=>9, :edit=>11, :new_private_message=>12, :got_private_message=>13, :solved=>15, :assigned=>16 --
    user_id integer NOT NULL,
    target_topic_id integer,
    target_post_id integer,
    target_user_id integer,
    acting_user_id integer,
    created_at timestamp without time zone NOT NULL,
    updated_at timestamp without time zone NOT NULL
);
SELECT * FROM user_actions WHERE id = 19928 OR id = 19929 OR id = 19931;
/*
아래 첫 번째 예시에서, id가 1인 사용자는 id가 11100인 게시글에 좋아요(action_type: 1)를 눌렀습니다. id가 1인 사용자가 이 작업을 수행했으므로 acting_user_id 컬럼의 값으로 1이 설정됩니다. 이 항목이 사용자가 "좋아요" 작업을 수행했다는 것을 기록하고 있으므로, 해당 사용자의 ID(1)도 항목의 user_id 컬럼에 설정됩니다.
id가 1인 사용자가 다른 사용자의 게시글에 좋아요를 누른 횟수를 반환하는 예제 쿼리: `SELECT COUNT(user_id) AS number_of_likes_given FROM user_actions WHERE action_type = 1 AND user_id = 1;`

아래 두 번째 예시에서, id가 121인 사용자의 게시글(id 11100)에 다른 사용자(action_type: 2)가 좋아요를 눌렀습니다. 게시글에 좋아요가 눌린 사용자의 id가 121이므로 항목의 user_id는 121로 설정됩니다. 게시글에 좋아요를 누른 사용자가 id 1이므로 항목의 acting_user_id 컬럼은 1로 설정됩니다.
id가 121인 사용자의 게시글이 다른 사용자들에게 의해 좋아요를 받은 횟수를 반환하는 예제 쿼리: `SELECT COUNT(user_id) AS number_of_likes_received FROM user_actions WHERE action_type = 2 AND user_id = 121;`
id가 121인 사용자의 게시글이 id가 1인 사용자로부터 좋아요를 받은 횟수를 반환하는 예제 쿼리: `SELECT COUNT(user_id) AS number_of_likes_received_from_user_1 FROM user_actions WHERE action_type = 2 AND user_id = 121 AND acting_user_id = 1;`

세 번째 예시에서, id가 121인 사용자는 id가 2인 사용자에게 id가 11101인 게시글에서 언급(action_type: 7)되었습니다. 언급된 사용자의 id가 121이므로 항목의 user_id는 121로 설정됩니다. 언급을 생성한 사용자의 id가 2이므로 항목의 acting_user_id는 2로 설정됩니다.
id가 121인 사용자가 어떤 사용자들에게든 언급된 횟수를 반환하는 예제: `SELECT COUNT(user_id) AS number_of_mentions_received FROM user_actions WHERE action_type = 7 AND user_id = 121;`
id가 121인 사용자가 id가 2인 사용자로부터 언급된 횟수를 반환하는 예제 쿼리: `SELECT COUNT(user_id) AS number_of_mentions_received_from_user_2 FROM user_actions WHERE action_type = 7 AND user_id = 121 AND acting_user_id = 2;`
id가 121인 사용자가 id가 7282인 주제 내에서 언급된 횟수를 반환하는 예제 쿼리: `SELECT COUNT(user_id) AS number_of_mentions_received_in_topic FROM user_actions WHERE action_type = 7 AND user_id = 121 AND target_topic_id = 7282;`
*/
  id   | action_type | user_id | target_topic_id | target_post_id | target_user_id | acting_user_id |         created_at         |         updated_at
-------+-------------+---------+-----------------+----------------+----------------+----------------+----------------------------+----------------------------
 19928 |           1 |       1 |            7282 |          11100 |                |              1 | 2023-08-14 07:47:49.520171 | 2023-08-14 07:47:49.651056
 19929 |           2 |     121 |            7282 |          11100 |                |              1 | 2023-08-14 07:47:49.520171 | 2023-08-14 07:47:49.671757
 19931 |           7 |     121 |            7282 |          11101 |                |              2 | 2023-08-14 08:00:05.877537 | 2023-08-14 08:00:05.877537

CREATE TABLE polls (
    id bigint NOT NULL,
    post_id bigint,
    name character varying DEFAULT 'poll'::character varying NOT NULL,
    close_at timestamp without time zone,
    type integer DEFAULT 0 NOT NULL,
    status integer DEFAULT 0 NOT NULL,
    results integer DEFAULT 0 NOT NULL,
    visibility integer DEFAULT 0 NOT NULL,
    min integer,
    max integer,
    step integer,
    anonymous_voters integer,
    created_at timestamp without time zone NOT NULL,
    updated_at timestamp without time zone NOT NULL,
    chart_type integer DEFAULT 0 NOT NULL, -- {"bar"=>0, "pie"=>1} --
    groups character varying,
    title character varying
);
SELECT * FROM polls WHERE id = 71 OR id = 72 OR id = 74;
 id | post_id | name |      close_at       | type | status | results | visibility | min | max | step | anonymous_voters |         created_at         |         updated_at         | chart_type |    groups     |                   title
----+---------+------+---------------------+------+--------+---------+------------+-----+-----+------+------------------+----------------------------+----------------------------+------------+---------------+-------------------------------------------
 71 |   11097 | poll | 2023-08-20 07:00:00 |    0 |      0 |       0 |          1 |     |     |      |                  | 2023-08-14 06:38:10.96388  | 2023-08-14 06:38:10.96388  |          0 | trust_level_2 | Who took the best picture?
 72 |   11098 | poll | 2023-09-10 07:00:00 |    0 |      0 |       0 |          1 |     |     |      |                  | 2023-08-14 06:40:36.925762 | 2023-08-14 06:40:36.925762 |          1 | staff         | Who should we invite to our next AMA?
 74 |   11100 | poll | 2023-08-27 07:00:00 |    1 |      0 |       0 |          0 |   1 |   2 |      |                  | 2023-08-14 06:48:27.498764 | 2023-08-14 06:48:27.498764 |          0 | eurorack      | What are your favourite Eurorack modules?

CREATE TABLE poll_options (
    id bigint NOT NULL,
    poll_id bigint,
    digest character varying NOT NULL,
    html text NOT NULL,
    anonymous_votes integer,
    created_at timestamp without time zone NOT NULL,
    updated_at timestamp without time zone NOT NULL
);
SELECT * FROM poll_options WHERE id = 271 OR id = 274 OR id = 280;
 id  | poll_id |              digest              |      html      | anonymous_votes |         created_at         |         updated_at
-----+---------+----------------------------------+----------------+-----------------+----------------------------+----------------------------
 271 |      71 | 5c6c2c880d86e5a0d9f0924a7ffd9629 | Sally          |                 | 2023-08-14 06:38:10.975263 | 2023-08-14 06:38:10.975263
 274 |      72 | 2654933188fb6bd444b6df4cc39fd908 | Dangerous Dave |                 | 2023-08-14 06:40:36.930354 | 2023-08-14 06:40:36.930354
 280 |      74 | f2e5462d97476629719e607dc80a9619 | Maths          |                 | 2023-08-14 06:48:27.502673 | 2023-08-14 06:48:27.502673

CREATE TABLE poll_votes (
    poll_id bigint,
    poll_option_id bigint,
    user_id bigint,
    created_at timestamp without time zone NOT NULL,
    updated_at timestamp without time zone NOT NULL
);
SELECT * FROM poll_votes WHERE poll_id = 71;
 poll_id | poll_option_id | user_id |         created_at         |         updated_at
---------+----------------+---------+----------------------------+----------------------------
      71 |            270 |       2 | 2023-08-14 06:59:00.382873 | 2023-08-14 06:59:00.382873
      71 |            271 |     121 | 2023-08-14 07:02:57.364892 | 2023-08-14 07:02:57.364892
      71 |            271 |       1 | 2023-08-14 07:03:36.464304 | 2023-08-14 07:03:36.464304

/* Discourse database documentation end */

이것은 users, groups, group_users, posts, topics, categories, tags, topic_tags, user_actions, polls, poll_options, 그리고 poll_votes 테이블을 다룹니다. 현재 1150개의 토큰을 차지하고 있으므로, 문제를 일으키지 않고 크기를 두 배로 늘릴 수 있어야 합니다. 참고로, 이를 ChatGPT 채팅 입력창에 복사해서 붙여넣을 경우, 채팅 입력창의 문자 수 제한이 채팅 세션의 토큰 제한보다 작으므로 두 개의 별도 입력창에 나누어 붙여넣어야 합니다.

예제 user_actions 쿼리 위에 상당히 상세한 주석을 추가했습니다. 이 예제 덕분에 ChatGPT는 좋아요를 준 횟수, 좋아요를 받은 횟수 등에 대한 질문에 잘 답변하고 있습니다. 이전에 이 부분에서 어려움을 겪고 있었습니다. 비슷한 접근 방식이 필요한 테이블이 몇 개 더 있을 것으로 추정됩니다.

문서화를 보낸 후, 다음과 같은 프롬프트가 도움이 됩니다:

예시 프롬프트

쿼리를 'query period CTE’로 시작하라고 요청할 때, 쿼리가 정확히 이 SQL(주석 포함)로 시작하기를 원합니다:

--[params]
-- string :query_interval = 1 week
-- date :start_date
-- date :end_date

WITH query_periods AS (
  SELECT
      generate_series(:start_date, :end_date, :query_interval::interval)::date AS period_start,
      (generate_series(:start_date, :end_date, :query_interval::interval)::date + :query_interval::interval - INTERVAL '1 DAY')::date AS period_end
)

Data Explorer 플러그인은 쿼리에 매개변수를 추가할 수 있습니다. 쿼리에서 사용되는 매개변수는 이 형식으로 쿼리 상단의 주석에 나타나야 합니다:

--[params]
--param_type :param_name

사용 가능한 매개변수 유형은 다음과 같습니다: int, bigint, boolean, string, date, time, datetime, double, user_id, post_id, topic_id, category_id, group_id, badge_id, int_list, string_list, user_list
다음은 예시입니다:

--[params]
-- string :action_type

매개변수에 선택적 기본값을 제공할 수 있습니다. 예를 들어:

--[params]
--string :action_type = like

“query_period CTE” 프롬프트를 만든 이유는, ChatGPT에게 query_period CTE를 만드는 방법에 대해 구체적으로 알려주지 않으면 다양한 해결책을 제시하는데, 그중 일부는 다른 것들보다 낫기도 했지만 일관성이 없었기 때문입니다. 이 프롬프트를 사용하면 제가 제공한 정확한 코드를 추가한 후, 그 위에 문제를 없이 쿼리를 구축합니다. 흥미롭게도, ChatGPT에게 처음 보낸 문서화에 "query period CTE"를 만드는 방법에 대한 세부 정보를 추가하려고 했을 때, ChatGPT는 지시사항을 무시했습니다. 어떤 이유에서인지 별도 프롬프트로 보내야 효과가 있습니다.

Discourse가 주제와 게시글을 "연삭(soft delete)"하는 방법에 대한 설명 프롬프트도 도움이 됩니다. 주제와 게시글 관련 쿼리에서 deleted_at IS NOT NULL인지 확인해야 한다는 것을 ChatGPT에게 알려준 후, ChatGPT는 모든 쿼리에 일관되게 이를 적용합니다.

ChatGPT에게 쿼리 끝에 세미콜론을 생략하라고 말하는 것은 거의 불가능합니다. 몇 개의 쿼리 동안은 기억하지만, 곧 다시 세미콜론을 추가합니다. 그것은 사소한 세부 사항인 것 같습니다.

자연어 질문에서 완벽한 쿼리가 반환되기를 바라는 제 초기 희망은 다소 야심 차 있었습니다. ChatGPT도 실수를 하고, 저도 실수를 합니다. 적어도 단기적으로는, Data Explorer 플러그인과 ChatGPT를 통합하는 가장 좋은 방법은 ChatGPT로 PM(개인 메시지)을 시작하는 것일 것입니다. PM이 생성될 때 데이터베이스의 기본 설명을 보낼 수 있습니다. 그런 다음 UI를 통해 프롬프트 선택지를 사용할 수 있게 할 수 있습니다. 예를 들어, 쿼리를 매개변수화하는 프롬프트나 덜 사용되는 테이블에 대한 세부 정보를 추가하는 프롬프트 등이 있습니다.

SQL에 대해 조금 아는 사람에게 매우 유용할 수 있다고 추정하지만, SQL에 익숙하지 않은 사람들이 혼자서 배우는 것보다 훨씬 더 빠르게 실력을 익힐 수 있도록 구현할 수도 있을 것입니다. 오늘 이 놀라운 트릭을 배웠습니다:

WITH post_type_mapping AS (
  SELECT 'regular' AS type, 1 AS post_type
  UNION ALL
  SELECT 'moderator_action' AS type, 2 AS post_type
  UNION ALL
  SELECT 'small_action' AS type, 3 AS post_type
  UNION ALL
  SELECT 'whisper' AS type, 4 AS post_type
)
...
JOIN post_type_mapping m ON :post_type = m.type
WHERE p.post_type = m.post_type
4개의 좋아요

혹시 제가 당신의 중독을 부추기고 있는 건 아닌지 걱정됩니다.

연구 논문을 읽으시는지는 모르겠지만, 오늘 자연어 쿼리를 입력하여 유효한 SQL과 결과를 생성하려는 목표 달성을 위해 새로운 방향을 제시해 줄 수 있는 또 다른 논문을 우연히 발견했습니다. 이 논문은 당신이 올바른 길을 가고 있다는 점을 다시 한번 확인시켜 줍니다.

해당 논문은 수학 문제를 다루고 있지만, 수학도 SQL과 마찬가지로 표현식이므로, 하나의 표현 형태를 다른 형태로 변경하면 충분히 적용이 가능할 것입니다. 유사한 아이디어를 가진 관련 논문이 많으므로, 이 한 편의 논문만을 권위 있는 것으로 간주하지 마시기 바랍니다.

“Solving Challenging Math Word Problems Using Gpt-4 Code Interpreter With Code-Based Self-Verification” (코드 기반 자기 검증이 포함된 Gpt-4 코드 인터프리터를 사용하여 어려운 수학 단문 문제를 해결하기) - Aojun Zhoun, Ke Wang, Zimu Lu, Weikang Shi, Sichun Luo, Zipeng Qin, Shaoqing Lu, Anya Jia, Linqi Song, Mingjie Zhan, Hongsheng Li (PDF)

1개의 좋아요

@simon

도와드릴 수 있는 또 다른 아이디어가 있는데, 이걸 제안하는 게 버섯을 기르는 것처럼 들릴 수도 있겠지만, 저는 실제로 Prolog 코드, 특히 웹 페이지 생성에 이 방법을 사용해 봤습니다.

문제는 생성형 AI가 선언형 언어인 SQL을 이해하는 데 더 어려움을 겪는다는 점일 수 있습니다. 생성형 AI가 프로그래밍 언어에서 성공을 거두는 이유는 JavaScript, Python, Java와 같은 몇 가지 명령형 프로그래밍 언어에 대한 대규모 학습 데이터셋 덕분입니다. 트랜스포머 기반으로 된 생성형 AI는(트랜스포머는 제 기억이 맞다면 영어에서 독일어 번역을 위해 처음 만들어졌습니다) 잘 학습된 프로그래밍 언어로의 번역과 그 역방향 변환에서 매우 뛰어난 성능을 보입니다. 따라서 처음부터 SQL을 요청하는 대신, 문제를 해결하기 위한 코드로 Python이나 다른 잘 학습된 프로그래밍 언어를 먼저 요청한 뒤, 생성형 AI가 그 Python 코드를 SQL로 번역하도록 해보고 그것이 작동하는지 확인해 보세요. 저는 이걸 시도할 계획은 없지만, 당신이 이걸 즐기시는 것 같으니 한번 해보시길 권합니다. 또한 이렇게 하신다면, 제가 반드시 결과를 알고 싶으므로 피드백을 꼭 남겨주세요. :slightly_smiling_face:

1개의 좋아요

이것은 오타인가요?

WHERE poll_id = 74가 WHERE poll_id = 71이어야 하는 건가요.


전체 프롬프트를 확인하지는 않았고, 쿼리 결과가 어떻게 보여야 하는지 예시를 포함했는지 확인하려고 했을 뿐입니다. 즉, 테이블 값의 예시는 주셨지만 프롬프트에 대한 기대 결과의 예시는 보이지 않았습니다. 아마 제가 놓쳤을 수도 있습니다. 기대 결과는 SQL이 올바른지 검증하는 데 사용할 수 있습니다.


제안:

Discourse database prompt 예시에서는 여러 테이블에서 id로 1이 사용되고 있습니다. 인간은 필드와 테이블의 컨텍스트 내에서 1이 가지는 의미를 이해하지만, AI는 이를 알지 못하므로 AI가 선택지를 만들 수 있는 기회를 줄 수 있습니다. 따라서 제안으로, Discourse database prompt를 수정하여 각 id에 서로 다른 숫자를 사용하도록 변경하십시오.

한 단계 더 나아가, OpenAI Tokenizer 페이지를 사용하여 각 예시 테이블의 모든 값이 단일 토큰인지 확인하십시오. 일부 값을 단어 또는 더 나쁘게는 문자열로 유지하고 싶을 수 있지만, AI가 정말로 그걸 중요하게 여기는 걸까요? 여러 토큰으로 구성된 값은 더 많은 변이와 가능한 환각(hallucinations)으로 이어질까요?

1개의 좋아요

실수였습니다. 수정하겠습니다. 다른 투표 관련 테이블과 관련된 결과를 가져오기 위해 쿼리를 71로 다시 실행해 본 것 같습니다.

블로그 게시물에서 모든 SELECT 쿼리를 다음과 같은 형태로 실행하라는 제안을 따르지 않았습니다:
SELECT * FROM polls LIMIT 3;
개발 데이터베이스에 익명화된 사용자와 삭제된 게시글이 많이 있기 때문에, 이렇게 하면 더 일관된 결과를 제공할 수 있을 것이라고 생각했습니다. 하지만 이제 새로 만든 데이터베이스로 프롬프트를 다시 작성하고 SELECT 문을 단순화해 보려고 합니다.

네, 모든 예시에서 세 명의 사용자를 사용하고 있습니다. 그들의 id는 1, 2, 121이므로 이 값들이 많이 반복됩니다. 일관된 데이터를 보여주는 것이 가장 좋다고 가정했습니다. 몇 가지 다른 접근법을 시도해 보고 어떤 것이 가장 잘 작동하는지 확인해 보겠습니다.

블로그 게시물에서 제안하는 또 다른 접근법은 프롬프트에서 설정되는 열의 수를 제한하는 것입니다. 이는 매력적이지만 많은 오류를 도입할 위험이 있고, 유지보수가 어려울 수 있습니다.

제가 생각하기에 보이는 패턴은, 복잡한 쿼리를 요청하며 세션을 시작하면 ChatGPT가 혼란스러워한다는 것입니다. 혼란을 극복하게 하면 세션의 나머지 부분에서 결과가 꽤 좋습니다. 잘 작동하는 것으로 보이는 다른 접근법은 간단한 쿼리로 시작하여 점차 더 복잡한 쿼리로 나아가는 것입니다. 이것이 실제 패턴인지, 아니면 성공률이 실제로는 보이는 것보다 더 무작위적인 것인지는 확신이 서지 않습니다.

현재 상태로는 SQL과 Discourse 데이터베이스에 이미 익숙한 사람들에게 유용할 것 같습니다. SQL이나 데이터베이스에 대해 잘 모르는 사람들에게도 유용할 수 있는 수준까지 발전했으면 합니다.

또한 ChatGPT-4로도 이를 테스트하고 있습니다. 더 나은 결과를 내줄 가능성이 높지만, 사용하면서 재미는 덜할 수 있습니다. ChatGPT-3.5는 훨씬 빠릅니다.

2개의 좋아요

몇십 년 전 데이터베이스를 배울 때, SQL만으로는 이해하기가 어려웠습니다. 하지만 나중에 Microsoft Access의 쿼리 빌더를 사용했는데, 여기서 테이블을 드래그 앤 드롭으로 배치한 후 테이블 필드를 선으로 연결하는 방식이었죠. Visio와 매우 유사한 방식이었고, 이렇게 하면 SQL이 자동으로 생성되었습니다.

비슷한 도구의 이미지입니다. 여기에서 가져왔습니다.

Discourse가 SQL을 구축하기 위한 이러한 도구를 만들 것이라고 기대하지는 않지만, AI를 통해 연결된 테이블의 이미지를 생성하여 피드백으로 제공하는 것은 가능할 것입니다.


기억이 맞다면 이 영상에서는 먼저 GPT-4를 사용하여 정확한 결과를 얻은 후, 속도를 위해 GPT-3.5에 맞게 커스터마이징하라고 언급하고 있습니다.

2개의 좋아요