2009-02-27 7 views
7

よくある問題ですが、ほとんどのソリューションは複数のSQLコマンドを連結することを指していますが、ADO/VBAではできないと思います(ただし、この点で間違って表示されることになります)。Excel VBAを使って新しく挿入されたレコードのIDを取得する方法は?

私は現在、私の新しいレコードを挿入し、新しく挿入されたレコードだけが返されることを保証するのに十分なフィールドを使って選択クエリを実行します。私のデータベースは一度に複数の人によってアクセスされることはほとんどありません(クエリ間で別の挿入が起こるリスクはほとんどありません)。テーブルの構造上、新しいレコードを特定することは通常非常に簡単です。

私は現在、擬似プライマリキー以外の一意性のスコープがあまりないテーブルを更新しようとしています。これは、新しいレコードがユニークではないというリスクがあることを意味し、一意性を強制するフィールドを追加するのは嫌です。

レコードをAccessテーブルに挿入し、この状況でExcelから新しいプライマリキーをクエリする最も良い方法は何ですか?

返信いただきありがとうございます。私は@@IDENTITYを動作させようとしましたが、これは以下のコードを使用して常に0を返します。

Private Sub getIdentityTest() 
    Dim myRecordset As New ADODB.Recordset 
    Dim SQL As String, SQL2 As String 

    SQL = "INSERT INTO tblTasks (discipline,task,owner,unit,minutes) VALUES (""testDisc3-3"",""testTask"",""testOwner"",""testUnit"",1);" 
    SQL2 = "SELECT @@identity AS NewID FROM tblTasks;" 

    If databaseConnection Is Nothing Then 
     createDBConnection 
    End If 

    With databaseConnection 
     .Open dbConnectionString 
     .Execute (SQL) 
     .Close 
    End With 

    myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly 

    Debug.Print myRecordset.Fields("NewID") 

    myRecordset.Close 

    Set myRecordset = Nothing 
End Sub 

何か目立つものはありますか?

しかし、Renaud(下記)が提供してくれた注意点を踏まえて、@@IDENTITYを他の方法と同じように使用すると危険性が高いようですので、今はSELECT MAXを使用しています。私は上記の私の試みに何が間違っているか見ることに興味があるが、将来の参照のために。

+0

Max'は注意が必要です '使い方:実際には、あなたもテーブルを指定する必要はありません。唯一レコードを挿入している場合は、進んでください。 –

答えて

12

私は今 ないというテーブルを更新しようとしています元気人工主キー 以外では 一意性の範囲が広いつまり、新しいレコード が一意でない可能性があります。 には、一意性を強制するためにフィールドを追加するのは嫌です。

あなたあるは、あなたの主キーの自動インクリメントを使用している場合は、あなたは独自性があり、あなたは(下記の注意事項を参照)、最後の自動生成されたIDの値を取得するためにSELECT @@Identity;を使用することができます。

あなたはは自動インクリメントを使用していないされ、あなたはAccessからレコードを挿入されていますが、Excelからの最後の1を取得する場合:

  • することができますので、あなたの主キーはソート可能であることを確認してくださいこれらのいずれかのようなクエリを使用して、最後の1を取得:

    SELECT MAX(MyPrimaryField) FROM MyTable; 
    SELECT TOP 1 MyPrimaryField FROM MyTable ORDER BY MyPrimaryField DESC; 
    
  • や、あなたの主なフィールドをソートすることはあなたの最後の1を与えないならば、あなたはDateTimeフィールドを追加する必要があります(たとえばInsertedDate)A ND現在の日付と時間にあなたがこのような最後のものを得ることができるようにあなたがそのテーブルに新しいレコードを作成するたびに保存します。これらのケースのいずれにおいても

    SELECT TOP 1 MyPrimaryField FROM MyTable ORDER BY InsertedDate DESC; 
    

を、私はあなたが追加することを見つけるだろうと思います対処するために多くの方が簡単であることなどの自動インクリメントの主キー:

  • は、あなたの費用ことはないだろうあまり

  • なくても、あなたの記録のあなたが一意性を保証するために起こっていますそれについて考えてください

  • @@Identityを使用するか、またはプライマリキーで並べ替えるか、Max()を取得すると、最新のレコードを簡単に選択できるようになります。

エクセルから

Excelにデータを取得するには、選択肢のカップルがあります:あなたは結果を使用できるように

  • は、クエリを使用してデータリンクを作成しますセルまたは範囲内に直接配置します。 VBAから

  • クエリ:あなたは、標準のAccessデータベース(*)で@@Identityを使用している場合careful of the caveatsする必要がおよそ@@Identity

    Sub GetLastPrimaryKey(PrimaryField as string, Table as string) as variant 
        Dim con As String 
        Dim rs As ADODB.Recordset 
        Dim sql As String 
        con = "Provider=Microsoft.ACE.OLEDB.12.0;" & _ 
          "Data Source= ; C:\myDatabase.accdb" 
        sql = "SELECT MAX([" & PrimaryField & "]) FROM [" & MyTable & "];" 
        Set rs = New ADODB.Recordset 
        rs.Open sql, con, adOpenStatic, adLockReadOnly 
        GetLastPrimaryKey = rs.Fields(0).Value 
        rs.Close 
        Set rs = Nothing 
    End Sub 
    

注:

  • AutoIncrement Identityフィールドでのみ機能します。

  • それはあなたがADOを使用してSELECT @@IDENTITY;

  • を実行する場合には、最新の使用するカウンタ、but that's for all tablesを返すのみ利用可能です。 MS Accessの特定のテーブルのカウンタを返すために使用することはできません(私が知る限り、FROM mytableを使用してテーブルを指定すると、無視されます)。
    要するに、返される値が期待どおりではない場合があります。

  • 間違った回答を得るリスクを最小限に抑えるため、INSERTの直後に問い合わせを行う必要があります。
    これは、データを一度に挿入し、別の時間(または別の場所)で最後のIDを取得する必要がある場合、そのIDは機能しません。

  • 最後に、プログラミングコードによってレコードが挿入された場合にのみ、変数が設定されます。
    これは、レコードがユーザーインターフェイスを通じて追加されたことを意味します。@@IDENTITYは設定されません。

(*):ちょうどあなたがあなたのデータベースのためのANSI-92 SQLモードを使用する場合@@IDENTITYは、異なる動作をし、より多くの予測方法で、明確にすること。
しかし、ANSI 92では、AccessでサポートされているANSI 89のフレーバーである とは少し異なる構文があり、フロントエンドとしてAccessを使用するとSQL Serverとの互換性が向上することが意図されています。

+1

「SELECT MAX」は本当に悪い匂いがします。説明されている状況では機能しますが、すべての事柄が時間の経過と共に変化していることを知る前に、このコードは失敗します。次の人について考えてください。 – TFD

+1

@TFD:原則的に同意しましたが、少なくともMS Accessでは、ポスターが望むものを達成するための方法はそれほどありません。常にトレードオフになります。 Max()は文字列を含むほとんどのデータ型で正常に動作しますが、あなたが望むものが得られない可能性があることに私は同意します。 –

+0

詳細な回答と、@@ IDENTITYに関する有益な注意点をありがとうございます - できるならば投票します。 – Lunatik

7

人工キーが自動番号である場合は、@@ identityを使用できます。

これらの両方の例では、トランザクションは他のイベントから分離されているため、返されたIDはちょうど挿入されたIDです。これをテストするには、Debug.Print db.RecordsAffectedまたはDebug.Print lngRecsでコードを一時停止し、手動でTable1にレコードを挿入し、コードを続けて、返されたIDが手動で挿入されたレコードのものではなく、前のコードによって挿入されたレコード。あなたの質問について

DAO例

'Reference: Microsoft DAO 3.6 Object Library ' 
Dim db As DAO.Database 
Dim rs As DAO.Recordset 

Set db = CurrentDb 

db.Execute ("INSERT INTO table1 (field1, Crdate) " _ 
      & "VALUES (46, #" & Format(Date, "yyyy/mm/dd") & "#)") 
Debug.Print db.RecordsAffected 
Set rs = db.OpenRecordset("SELECT @@identity AS NewID FROM table1") 
Debug.Print rs.Fields("NewID") 

ADO例

Dim cn As New ADODB.Connection 
Dim rs As New ADODB.Recordset 

Set cn = CurrentProject.Connection 

cn.Execute ("INSERT INTO table1 (field1, Crdate) " _ 
      & "VALUES (46, #" & Format(Date, "yyyy/mm/dd") & "#)"), lngRecs 
Debug.Print lngRecs 
rs.Open "SELECT @@identity AS NewID FROM table1", cn 
Debug.Print rs.Fields("NewID") 
+0

ありがとう、これを試してみましょう。 – Lunatik

+0

これで、「引数が間違った型か、許容範囲外か、互いに競合しています」という3001エラーが発生します。私はまた、オブジェクトブラウザで「OpenRecordset」を見つけることができません。 私はADO 2.6ライブラリを参考にしています。 – Lunatik

+0

コードはDAO用で、通常はMS Accessに最適です。必要に応じて、私はADOで何かを実行する必要があります。 – Fionnuala

0

は、マクロcode.Firstは、コントロールボックスからシートにコマンドボタンを追加し、

Private Sub CommandButton1_Click() 
    MsgBox GetLastPrimaryKey 
End Sub 

Private Function GetLastPrimaryKey() As String 
Dim con As String 
Dim cn As ADODB.Connection 
Dim rs As ADODB.Recordset 
Dim sql As String 
con = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\myaccess.mdb;Persist Security Info=False" 
sql = "SELECT MAX(id) FROM tblMyTable" 

Set cn = New ADODB.Connection 
Set rs = New ADODB.Recordset 
cn.Open con 
rs.Open sql, cn, 3, 3, 1 
If rs.RecordCount <> 0 Then 
    GetLastPrimaryKey = rs.Fields(0).Value 
End If 
rs.Close 
cn.Close 
Set rs = Nothing 
Set cn = Nothing 
End Function 
3

コードウィンドウに以下のコードを貼り付け、次の試してみてください日時:「私は、@@ IDENTITYの作業を取得しようとしていますこれは以下のコードを使用して常に0を返します。

あなたのコードは、異なる接続オブジェクトを通じてSQLSQL2を送信します。あなたがINSERTステートメントを実行したのと同じ接続から質問しない限り、@@identityはゼロ以外の何かを返すとは思わない。

これを変更してみてください:

myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly 

に:

myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly 
+0

D'oh、これは、私が別のプロジェクトのコードを再利用するためのものです!私は現時点で別のプロジェクトを行っていますが、あなたの提案は完璧な意味合いを持っています。 – Lunatik

0

ここ@@インデックスまたはMAXを使用していない私の解決策です。

Const connectionString = "Provider=SQLOLEDB; Data Source=SomeSource; Initial Catalog=SomeDB; User Id=YouIDHere; Password=YourPassword" 
Const RecordsSQL = "SELECT * FROM ThatOneTable" 

Private Sub InsertRecordAndGetID() 
    Set connection = New ADODB.connection 
    connection.connectionString = connectionString 
    connection.Open 
    Set recordset = New ADODB.recordset 
    recordset.Open SQL, connection, adOpenKeyset, adLockOptimistic 

    With recordset 
     .AddNew 
     !Field1 = Value1 
     !Field2 = Value2 
    End With 

    recordset.MoveLast 
    ID = recordset.Fields("id") 

End Sub 

お楽しみください!

0

締め切り8年...問題は、dbConnectionStringを使用しての新規接続を作成していることです。 @@ identityは、使用している接続に固有です。

まず、

'.Close 

は、あなたが以前に挿入

myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly 

のために使用された接続で

myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly 

を置き換える元の接続を閉じないと、あなたはされていると思います準備完了。他の誰かがレコードを挿入されている場合、それはあなたに別のレコードのIDを与えることがあります...

SQL2 = "SELECT @@identity AS NewID"