Microsoft Access 掲示板

単純なキーブレイク処理

9 コメント
views

単純なキーブレイク処理処理のはずなのですが、ロジックが構築できません。
対称はテーブルで、Noはレコードの識別子です。
Noが連続しない場合(固有のレコード)、は削除したいです。
ループ処理の中で、1,2,3、1、1、1,2とカウントを数えて、
カウントが1だったら削除というようなロジックになるだろうと考えているのですが、
キーが切れた時に、
直前まで持っていたカウントが1なら
rs.MovePrevious、
rs.delete、
rs.MoveNext
カウント初期化にようなロジックではどうもうまくいきません。


N0 Cnt

107 1
107 2
107 3
110 1・・・削除
111 1・・・削除
123 1
123 2

アドバイスよろしくお願いいたします。

はづき
作成: 2025/05/09 (金) 16:59:13
最終更新: 2025/05/09 (金) 16:59:50
通報 ...
1
はづき 2025/05/09 (金) 17:17:00 69f3e@da8eb

コードはこちらです。

Funcion keyBreak()

  Dim rs As DAO.Recordset
  Dim PreNo As Long
  Dim strSQL As String

  strSQL = "select * from Mtbl_社員 ORDER BY No"

  Set rs = CurrentDb.OpenRecordset(strSQL, dbOpenDynaset, dbFailOnError)

  cnt = 0

  Do Until rs.EOF

    If PreNo = rs!No Then

      cnt = cnt + 1

    Else

      If cnt = 1 Then

        rs.MovePrevious

          rs.Delete

        rs.MoveNext

      End If

      PreNo = rs!No

      cnt = 1

    End If

    rs.MoveNext

  Loop

  rs.Close
  Set rs = Nothing

End Function

2

select * from Mtbl_社員 ORDER BY No

No は Access SQL における予約語の 1 つであり、
Yes/No 型( VBA における Boolean 型)の False 値、
数値型の 0 と同義です。

(最初からそんな名前はつけない方がよいですが)
フィールド名として識別させたいのであれば、[]で囲ってください。

select * from Mtbl_社員 ORDER BY [No]

対称対象はテーブルで、Noはレコードの識別子です。

テーブル[Mtbl_社員]の主キーはフィールド[No]である、
という意味だとして、

Noが連続しない場合(固有のレコード)、は削除したいです。

107 1
107 2
107 3
110 1・・・削除
111 1・・・削除
123 1
123 2

これはどのテーブルのレコードのことをおっしゃっているのでしょうか。

[No]が主キーであれば、[No]の値が他のレコードと重複するレコードを
テーブル[Mtbl_社員]に格納することは出来ないはずですが。

3
はづき 2025/05/09 (金) 18:06:28 69f3e@da8eb >> 2

実際のコードとは違いまして、簡略化したコードを載せています。
ご指摘の通り、Noは使っていません。

Noは社員コードの意でして、主キーではありません。
Mtbl_社員(現所属)の社員コードとご理解ください。
簡易チェックのためのテンポラリーの抽出で、所属が専任ではなく、兼任の人を抽出したいという意図があります。

一応、このコードでも削除自体はできていまして、
ただ、あまりいいやり方ではないのかなという懸念と、
最終レコードの社員が1レコードのみの場合の削除ができていないという詰めが甘いです。

ループを回す前に、Movelastをして、最終レコードの社員が1レコードの場合は削除、
MoveFirstとするようなお粗末なやり方を考えました。

4

Noは社員コードの意でして、主キーではありません。

では、以下[社員コード]と読み替えます。

所属が専任ではなく、兼任の人を抽出したい

単純に「[Mtbl_社員]に格納されているレコードのうち、
[社員コード]の値が同一であるレコード同士を 1 つのグループとみなし、かつ
『レコード件数が 2 件以上であるグループ』に含まれるレコードを抽出したい」
ということでしょうか。

SELECT [Mtbl_社員].* 
FROM [Mtbl_社員] 
INNER JOIN 
    (SELECT tmp.[社員コード] 
     FROM [Mtbl_社員] AS tmp 
     GROUP BY tmp.[社員コード] 
     HAVING Count(*) > 1) AS [兼任社員抽出クエリ] 
ON [Mtbl_社員].[社員コード] = [兼任社員抽出クエリ].[社員コード]
ORDER BY [Mtbl_社員].[社員コード];

例えば、以上のような選択クエリを作成なされば同様の結果が得られるはずです。

簡易チェックのためのテンポラリーの抽出

抽出されたレコードに対し、更に何らかの編集操作を行いたい場合は、
上記の選択クエリの結果を同一構造の一時テーブルに出力するように
なさればよいでしょう。

5
はづき 2025/05/09 (金) 18:58:27 69f3e@da8eb >> 3

はいその通りでして、当初SQLでやろうとして断念し、作業テーブルに書き出したものを、
VBAで削除するしかできませんでした。

すごいですね。これでできてしまうのですか!
こちらを採用させていただきます。

6

下記のSQLでもskさんのクエリと同じ結果を得られます。
メリットとしては、このクエリの場合は編集操作が可能です。
デメリットとしては、データ数が多いとskさんのより重くなる可能性があります。

SELECT Mtbl_社員.*
FROM Mtbl_社員
WHERE DCount("社員コード","Mtbl_社員","社員コード=" & [社員コード])>1;
7
はづき 2025/05/10 (土) 06:04:51 69f3e@da8eb >> 3

ありがとうございます。
最初、Dcountが直感的にはやりやすいので思い浮かびましたが、
これまでの経験上、D系を使うとかなりストレスフルなのを感じていましたので、
レコードが2万件くらいあるので、やりずらかったです(なので試してもいなかったです)。
HAVINGのほうは、それほどストレスなく使えました。

8
hatena 2025/05/10 (土) 10:04:29 修正 >> 3

抽出したデータの閲覧だけならskさんのサブクエリを使う方法でいいと思います。

更新する必要があり、かつ、ストレスなく使いたいという場合は、私なら下記の方法をとります。

下記のような[社員コード]のみの一時テーブルを作成しておきます。

テーブル名: Tmp_兼任社員コード
フィールド: 社員コード (主キー)

下記のクエリを順次実行します。

削除クエリ: 一時テーブルのデータを削除

DELETE Tmp_兼任社員コード.*
FROM Tmp_兼任社員コード;

追加クエリ: 一時テーブルに兼任社員コードを追加

INSERT INTO Tmp_社員コード ( 社員コード )
SELECT 社員コード
FROM Mtbl_社員
GROUP BY 社員コード
HAVING Count(*)>1;

兼任社員抽出クエリ

SELECT Mtbl_社員.*
FROM Mtbl_社員 INNER JOIN Tmp_社員コード
 ON Mtbl_社員.社員コード = Tmp_社員コード.社員コード;

やっていることはskさんのSQLのサブクエリ部分を一時テーブルにしただけです。
これでストレスフリーで編集作業も可能になります。

9
はづき 2025/05/10 (土) 13:04:24 69f3e@da8eb >> 3

確かにです、一時テーブルに社員コードだけを格納してINNER JOINですね。
パフォーマンスを落とさず、クエリが読み取り専用になってしまう場合などは、有効ですね。