1

2 つのテーブルがあります。

車両:

  • ID
  • 登録番号
  • LastAllocationUserName
  • LastAllocationDate
  • LastAllocationId

割り当て:

  • ID
  • 車両 ID
  • ユーザー名
  • 日にち

Vehicle テーブルのすべての行を最新の割り当てで更新する最も効率的な (最も簡単な) 方法は何ですか? SQL Server では、UPDATE FROM を使用して、すべての車両を最新の割り当てで結合します。Oracle には UPDATE FROM がありません。Oracleでどのようにしますか?

** 編集 **

更新に最適な SQL クエリを求めています。トリガーを使用して、マスター テーブルのデータを更新します。トリガーの書き方を知っています。私が求めているのは、Vehicle テーブルを更新するための SQL クエリの書き方だけです。例はいいでしょう。ありがとうございました。

4

6 に答える 6

2

現在のセットアップでは、ALLOCATIONS テーブルでトリガーを使用して、不適切な決定の選択を維持する必要があります。つまり、次を使用します。

UPDATE VEHICLE
   SET (LastAllocationUserName, LastAllocationDate, LastAllocationId) =
       (SELECT a.username,
               a.date,
               a.id
          FROM ALLOCATIONS a
          JOIN (SELECT b.vehicleid, 
                       MAX(b.date) AS max_date
                  FROM ALLOCATIONS b
              GROUP BY b.vehicleid) x ON x.vehicleid = a.vehicleid
                                     AND x.max_date = a.date
         WHERE a.vehicleid = VEHICLE.id)

これは、問題のある列を VEHICLE テーブルから削除し、ビューを使用して最新の割り当て情報を提供することにより、より適切に処理されます。

于 2010-05-13T20:40:32.707 に答える
2

他のほとんどの人が示しているように、データ モデルが原因で大きな問題が発生しています。このモデル用に書かれたほとんどのコードは、必要以上に難しくなります。私は賛成票と反対票を投じて、またいくつかのコメントでもそれを言いましたが、十分に言うことはできません.

パスを続行する場合、以下のコードは何をする必要があるかを示しています。うまくいけば、それはあなたを怖がらせます:-)

サンプル テーブル:

SQL> create table vehicles (id,registrationnumber,lastallocationusername,lastallocationdate,lastallocationid)
  2  as
  3  select 1, 1, 'Me', sysdate-1, 2 from dual union all
  4  select 2, 2, 'Me', sysdate, 3 from dual
  5  /

Table created.

SQL> create table allocations (id,vehicleid,username,mydate)
  2  as
  3  select 1, 1, 'Me', sysdate-2 from dual union all
  4  select 2, 1, 'Me', sysdate-1 from dual union all
  5  select 3, 2, 'Me', sysdate-1 from dual
  6  /

Table created.

トリガーは、最後の割り当てを決定するために、独自のテーブルを調べる必要があります。Oracle は、変更テーブル エラーを発生させることにより、このタイプのダーティ リードを防止します。これを回避するために、SQL 型とパッケージを作成します。

SQL> create type t_vehicle_ids is table of number;
  2  /

Type created.

SQL> create package allocations_mutating_table
  2  as
  3    procedure reset_vehicleids;
  4    procedure store_vehicleid (p_vehicle_id in vehicles.id%type);
  5    procedure adjust_vehicle_last_allocation;
  6  end allocations_mutating_table;
  7  /

Package created.

SQL> create package body allocations_mutating_table
  2  as
  3    g_vehicle_ids t_vehicle_ids := t_vehicle_ids()
  4    ;
  5    procedure reset_vehicleids
  6    is
  7    begin
  8      g_vehicle_ids.delete;
  9    end reset_vehicleids
 10    ;
 11    procedure store_vehicleid (p_vehicle_id in vehicles.id%type)
 12    is
 13    begin
 14      g_vehicle_ids.extend;
 15      g_vehicle_ids(g_vehicle_ids.count) := p_vehicle_id;
 16    end store_vehicleid
 17    ;
 18    procedure adjust_vehicle_last_allocation
 19    is
 20    begin
 21      update vehicles v
 22         set ( v.lastallocationusername
 23             , v.lastallocationdate
 24             , v.lastallocationid
 25             ) =
 26             ( select max(a.username) keep (dense_rank last order by a.mydate)
 27                    , max(a.mydate)
 28                    , max(a.id) keep (dense_rank last order by a.mydate)
 29                 from allocations a
 30                where a.vehicleid = v.id
 31             )
 32       where v.id in (select column_value from table(cast(g_vehicle_ids as t_vehicle_ids)))
 33      ;
 34    end adjust_vehicle_last_allocation
 35    ;
 36  end allocations_mutating_table;
 37  /

Package body created.

次に、3 つのデータベース トリガーを使用して、更新コードを行レベルからステートメント レベルに移動し、変更テーブル エラーを回避します。

SQL> create trigger allocations_bsiud
  2    before insert or update or delete on allocations
  3  begin
  4    allocations_mutating_table.reset_vehicleids;
  5  end;
  6  /

Trigger created.

SQL> create trigger allocations_ariud
  2    after insert or update or delete on allocations
  3    for each row
  4  begin
  5    allocations_mutating_table.store_vehicleid(nvl(:new.vehicleid,:old.vehicleid));
  6  end;
  7  /

Trigger created.

SQL> create trigger allocations_asiud
  2    after insert or update or delete on allocations
  3  begin
  4    allocations_mutating_table.adjust_vehicle_last_allocation;
  5  end;
  6  /

Trigger created.

そして、単一ユーザー環境で動作することを確認するための簡単なテスト:

SQL> select * from vehicles
  2  /

        ID REGISTRATIONNUMBER LA LASTALLOCATIONDATE  LASTALLOCATIONID
---------- ------------------ -- ------------------- ----------------
         1                  1 Me 13-05-2010 14:03:43                2
         2                  2 Me 14-05-2010 14:03:43                3

2 rows selected.

SQL> insert into allocations values (4, 1, 'Me', sysdate)
  2  /

1 row created.

SQL> select * from vehicles
  2  /

        ID REGISTRATIONNUMBER LA LASTALLOCATIONDATE  LASTALLOCATIONID
---------- ------------------ -- ------------------- ----------------
         1                  1 Me 14-05-2010 14:03:43                4
         2                  2 Me 14-05-2010 14:03:43                3

2 rows selected.

SQL> update allocations
  2     set mydate = mydate - 2
  3   where id = 4
  4  /

1 row updated.

SQL> select * from vehicles
  2  /

        ID REGISTRATIONNUMBER LA LASTALLOCATIONDATE  LASTALLOCATIONID
---------- ------------------ -- ------------------- ----------------
         1                  1 Me 13-05-2010 14:03:43                2
         2                  2 Me 14-05-2010 14:03:43                3

2 rows selected.

SQL> delete allocations
  2   where id in (2,4)
  3  /

2 rows deleted.

SQL> select * from vehicles
  2  /

        ID REGISTRATIONNUMBER LA LASTALLOCATIONDATE  LASTALLOCATIONID
---------- ------------------ -- ------------------- ----------------
         1                  1 Me 12-05-2010 14:03:43                1
         2                  2 Me 14-05-2010 14:03:43                3

2 rows selected.

あとは、シリアライゼーションを追加して、マルチ ユーザー環境で 100% 動作させるだけです。しかし、うまくいけば、この例はそのままで十分に恐ろしいものでした.

よろしく、ロブ。

于 2010-05-14T12:19:09.607 に答える
2

設計の観点からは、Vehicle テーブルで 3 つのフィールドをアクティブに維持し、'Allocations' を履歴テーブルとして (おそらくトリガーによって) 設定することをお勧めします。親テーブルの更新を子の挿入にプッシュする方が、その逆よりもはるかに簡単です。

于 2010-05-14T00:53:37.340 に答える
0

トリガー内のアップデートをお探しですか?

CREATE TRIGGER ALLOCATION_I
AFTER INSERT ON ALLOCATION
REFERENCING NEW AS NEW
FOR EACH ROW
Begin

UPDATE Vehicle 
 set LastAllocationUserName = :NEW.Username 
 ,LastAllocationDate = :NEW.date 
 ,LastAllocationId = :NEW.id 
WHERE Id = :NEW.VehicleId;

END;
于 2010-05-14T08:45:11.620 に答える
0

Oracle で別のテーブルを使用して「更新」する最も簡単な方法は、MERGE を使用することです。

MERGE INTO vehicle v
USING (
  -- subquery to get info you need
) s ON (v.id = s.vehicleId)
WHEN MATCHED THEN UPDATE SET 
  username = s.username 
  ...

http://psoug.org/reference/merge.html

于 2010-05-13T20:43:45.440 に答える
-1
UPDATE VEHICLE V
   SET (LastAllocationId, LastAllocationDate, LastAllocationUserName) =
   (SELECT a.id
           ,a.date
           ,a.username
      FROM ALLOCATIONS a
      where a.VehicleId = V.id
        and a.date = ( select max(Last_a.date) from ALLOCATIONS Last_a
                       where Last_a.VehicleId = V.id )
    )

あなたが正しい。履歴テーブルを含むビューは遅いです。高速な「最後のレコードへの結合」などはありません。トリガーは最良のソリューションです。可能であれば、PL を使用して最初に入力します。理解しやすく、管理しやすいです。

DECLARE
   Last_date DATE;
   Last_User Varchar2(100);
   Last_ID number;
Begin
FOR V IN ( Select * from VEHICLE )
LOOP
   select max(date) into Last_date 
   from ALLOCATIONS Last_a
   where Last_a.VehicleId = V.id;

   IF Last_date is NULL then 
      Last_User := NULL;
      Last_ID := NULL;
   else
      select Id,UserName into Last_id, Last_user
      from ALLOCATIONS Last_a
      where Last_a.VehicleId = V.id
      and Last_a.date = Last_date;
   END IF;

   UPDATE Vehicle 
     set LastAllocationUserName = Last_User
         ,LastAllocationDate = Last_date
         ,LastAllocationId Last_id
   Where id = V.id;

END LOOP;
End;

警告: ここに書かれていますが、テストされていません。

于 2010-05-14T08:05:19.343 に答える