2

OK、PostgresQL でユーザー データベースを作成しています。これは 1、2 か月使用していますが、あまり慣れていません。現在、登録する各ユーザーには、PostgresQL シーケンスを介して一意の ID が自動的に割り当てられます。これは常に実践されており、常にうまく機能していますが、... クライアントから、ユーザーの特定の ID を手動で入力できるようにするようにという要求があります。300 人を超えるユーザーは想定していないので、手動で入力する ID 用に ID 500 ~ 600 を予約していました。(オンラインで登録すると、自動インクリメントされた番号が取得されます。ほとんどの場合、300 を超えることはありません。手動 ID が必要な場合は、500 から 600 の間の事前に決定された ID のみが与えられます。) はい、99.9999% 確信しています。 「auto-id」ユーザーは 300 人を超えることはありません。

私が望む任意の手動 ID を提供できるようにしたいのですが、オンラインで登録し、ID が既に取得されている場合は、次の利用可能な ID を取得します。これはシーケンスの目的に反することはわかっていますが、他のオプションが何であるかはわかりません。発行できる ID の数には限りがあるので、できれば「最大 + 1」ではなく、「ギャップを埋めたい」と考えています。IDが手動で入力されたときにシーケンスを更新することのどこかに答えがあると確信していますが、それは悪い考えのように聞こえます.理由はわかりません.

それが良いアイデアでない場合は、「おい、お前は馬鹿だ。誰がお前をデータベースの責任者にしたの?」と言ってください。そして、私たちは皆、私たちの一日を過ごします。御時間ありがとうございます。

4

3 に答える 3

3

おそらくDROPシーケンスを完全ALTER TABLEに削除し、列からデフォルトを削除します。次に、 の間にINSERT、次のようなものを書きます。

BEGIN;
LOCK TABLE users IN EXCLUSIVE MODE;
INSERT INTO users (user_id, blah, blah)
VALUES (
   coalesce(
       requested_id_or_null_if_none_supplied,
       (SELECT coalesce(max(user_id),0) FROM users)+1
   ),
   'blah',
   'blah'
);
COMMIT;

これは同時実行性が悪いです。任意の時点で 1 つのトランザクションだけがユーザーを挿入できます。あなたが扱っているボリュームを考えると、トランザクションを短くしておく限り、それは完全に問題ないはずです.

データベースの内部主キーとは別に、公開表示識別子として使用される新しいフィールドを追加することを検討してください。このフィールドは、顧客の目的で表示するために使用します。好きなものを入れて、適切な一意のキーの一部として宣言するだけです。UNIQUE

これを行うと、シーケンスを使用して基になる番号の割り当てを同じに保つことができるため、DB の他の場所でユーザーを参照する方法を変更する必要はありません。アプリの残りの部分が内部でユーザーを参照する方法を変更する必要はありません。ユーザー識別子を表示する場合は、表示用の「ユーザー番号」を検索して使用します。ユーザーがユーザー番号を入力すると、一致する行の主キーを検索します。ユーザー番号は、エンドユーザーの識別子として入出力にのみ必要であり、データベースの残りの部分では参照されません。

これが、データベース/アプリ設計の知恵が、一般的に生成されたキーをユーザーに公開すべきではないことを強調する理由です。最終的に、彼らがそれらを見ることができれば、彼らは必然的に「楽しい」結果でそれらを変更できるようにしたいと思うでしょう. 彼らに独自の公開識別子を与える方がはるかに簡単です。彼らが突然英数字にすることに決めたが、それは満月のある火曜日だけ - それはOKです。

「表示識別子」と「一意の内部行キー」を別々のものに分割したら、簡単に変更できるようにするなど、表示識別子を自由に使用できます。

于 2012-10-03T23:27:25.093 に答える
2

シーケンスの適切な使用方法についてレクチャーを受けたことは明らかです。あなたは質問にいくつかの洞察を示し、@Craigの答えはギャップを包括的にカバーしています。

あなたの質問に関しては、私はそれを逆に考えます:
特別なゲストのために特別な番号のプールを予約してください. 特殊番号は短くする必要があります。
たとえば、10000(あなたの場合)でシーケンスを開始します。倹約する必要はありません。シーケンス番号は安くて豊富です。

CREATE TABLE usr (
  usr_id serial PRIMARY KEY
 ,usr text   -- UNIQUE??
);

-- Let sequence start at 10000
SELECT setval('usr_usr_id_seq', 10000, FALSE);

-- Init table with first user if you want to start at certain number
-- Else numbers start at the lowest manual entry.
INSERT INTO usr(usr_id, usr) VALUES (1, 'first_user');

特別な INSERT (要求された番号が利用できない場合は、最初の空き番号を取る):

WITH x AS (SELECT 4::int AS usr_id, 'special_guest' AS usr)
INSERT INTO usr(usr_id, usr)
SELECT CASE
         WHEN x.usr_id IS NULL THEN nextval('usr_usr_id_seq'::regclass)
         WHEN EXISTS (SELECT 1 FROM usr u WHERE u.usr_id = x.usr_id) THEN (
            SELECT u.usr_id + 1
            FROM   usr u
            WHERE  NOT EXISTS (SELECT 1 FROM usr u1
                               WHERE  u1.usr_id = u.usr_id + 1)
            ORDER  BY u.usr_id
            LIMIT  1)
         ELSE x.usr_id
       END
      ,x.usr
FROM   x;

ジェネリック INSERT (シーケンスから番号を取得):

INSERT INTO usr(usr) VALUES ('unspecial_guest');

-> sqlfiddle デモ

同時実行

並行性について心配する必要がある場合は、最初からこれを使用しないでください。
このセットアップは、特殊なケースでいっぱいのハンド用です。

于 2012-10-04T01:14:58.300 に答える
1

あなたの問題はシーケンスとは何の関係もないと思います。負の数を生成するシーケンスを作成できます。そうすれば、手動識別子と自動識別子を完全に分離できます。しかし、正の ID の中から「次に利用可能な番号」を見つける必要があるため、これでは問題は解決しません。

ソリューションは、ユーザーの数によっても異なります。最大1000人のユーザーしかいない場合。次に、for ループを使用して「次に大きい使用可能な数値」を決定できます。しかし、ランダムな (手動で与えられた) 識別子を持つ 100 万人のユーザーがいる場合、問題はそれほど簡単ではありません。

ここの概念がわかりません。彼らは手動で番号を与えるか、与えないかのどちらかです。番号がすでに使用されている場合、なぜ最も近い番号を見つけたいのでしょうか? シーケンスによって返される他の数値よりも優れているのはなぜですか? つまり、手動で入力した番号が既に使用されている場合は、ユーザーにエラー メッセージを表示できます。ユーザーが指定された番号を主張しない場合は、他の番号で構いません。右?

于 2012-10-03T17:04:46.767 に答える