Microsoft Access 掲示板

カラムにデータがあったら、別のカラムにデータを入力する方法

26 コメント
views
4 フォロー

現在の、4打刻制のタイムカードです。
これを、6打刻制に変更するにあたり、
休憩開始のカラムが空欄もしくは、Nullだったらデータを更新して、
休憩開始のカラムに、データがあったら休憩開始2のカラムにデータを入力するにはどうしたら良いのでしょうか?
一度、Excellに出力すれば簡単ですが、出力しない前提の方法を探しています。
よろしくお願い致します。

Sub restin()

Dim adoCn As Object
Dim d As Variant
Dim d1 As String
Dim d3 As String

    Set adoCn = CreateObject("ADODB.Connection") 'ADODBコネクションオブジェクトを作成
    adoCn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\tasuk\Desktop\VBA\経理\出勤\出勤 - コピー.accdb;" 'Accessファイルに接続

    With Sheets("入力")

     d = Date
     jan = .Cells(1, 2).Value 'JANコード取得
     d1 = .Cells(10, 3).Value '開始時刻取得

    Tn = "出勤データ"
    Cd = " SET [休憩開始] = #" & d1 & "#"
    Sc = " 月日 = " & "#" & d & "# and jan = '" & jan & "'" '最終検索条件の確定

    Sc = " WHERE" & Sc
    Call kousin1(Tn, Cd, Sc, adoCn) 'データを追加する

    End With

    adoCn.Close
    Set adoCn = Nothing

End Sub

Function kousin1(ByVal Tn As String, ByVal Cd As String, ByVal Sc As String, ByRef adoCn As ADODB.Connection)

    strSQL = "UPDATE " & Tn & Cd & Sc

    'Debug.Print strSQL

    adoCn.Execute strSQL, lRecordAffected 'SQLを実行してレコードを更新

    If lRecordAffected = 0 Then
        MsgBox "対象レコードが存在しませんでした。"
         'Debug.Print Sc
          Exit Function
       End If

    Set Object = Nothing

End Function

タークン
作成: 2023/12/04 (月) 16:02:20
通報 ...
1
りんご 2023/12/04 (月) 19:04:44 935bc@0e907

データは縦に持つので、そんな事はやりません。正規化して下さい。

2
すずやん 2023/12/05 (火) 09:06:54

入力データと完成させたいデータの図がいまいち見えないので予想でしか無いですが。

タイムカードであればそれほどデータ量が無いと思うので、一度テーブルに全部展開して、その後処理すればいいんではないでしょうか。

どのカラムが休憩開始のカラムだとかも決め打ちで分かるだろうし。

3
タークン 2023/12/05 (火) 11:38:05 7a0a1@2705a

質問に使った言葉が間違っていました。
カラムではなくフィールドでした。

今回のデータ量であれば、いかようにでも処理はできます。
ここで質問させていただいたのは、2023/12/15の休憩時間1のフィールドにデータが無かったら(NULLを含む)更新しないで、休憩時間2のフィールドにデータを登録する方法が知りたかったのです。

2023/12/15の休憩時間1のフィールドのデータの有無を調べるSQLを探してみたのですが、見つかりませんでした。フィールドが空だったら更新するが、フィールドにデータが入っていたら更新しないというのは、何かと利用する機会があるような気がするのです。

4
hiroton 2023/12/05 (火) 12:27:41 049d8@f966d

基本的には更新するデータとして抽出しないだけでしょう

WHERE Not 休憩時間1 Is Null

複数のフィールドを同時に更新するのでレコードとして省きたくないのであれば、更新しない=元の値で更新するとすれば同じになるので

休憩時間2 = Nz([休憩時間1]),[休憩時間2])

とかですかね

コードで記述するならRecordsetオブジェクトに取り込んでレコードをループ処理で処理したほうがいいと思いますが

5
タークン 2023/12/05 (火) 18:40:39 7a0a1@2705a

よく理解していないので、間違っていたら申し訳ありません。
WHERE Not 休憩時間1 Is Null
WHERE 休憩時間1 != ''
だと、更新するかしないかの判断になると思いますが、勘違いでしょうか?

休憩時間2 = Nz([休憩時間1]),[休憩時間2])
だと、
休憩時間1が空だったら、休憩時間2の時間という事になって、
いづれにしても、休憩時間2のフィールドが更新されるような気がしますが
これも、勘違いでしょうか?

休憩時間1が、Nullか空の場合は、
  SET [休憩開始] = #2023/12/05 17:00:00#
にして
    strSQL = "UPDATE 出勤データ SET [休憩開始] = #2023/12/05 17:00:00# WHERE 月日 = #2023/12/05# and jan = '1234567891123'
を実行

休憩時間1に、データがあった場合は、
  SET [休憩開始2] = #2023/12/05 17:00:00#
にして
    strSQL = "UPDATE 出勤データ SET [休憩開始2] = #2023/12/05 17:00:00# WHERE 月日 = #2023/12/05# and jan = '1234567891123'
を実行

    adoCn.Execute strSQL, lRecordAffected 'SQLを実行してレコードを更新

という感じの処理を行いたいのです。
一度、シートに展開してしまえば、造作もないことですが、
可能な限り、Excellのセルには触れたくないと感じ始めたので、こだわっています。

8
hiroton 2023/12/06 (水) 10:16:13 ccda7@f966d >> 5

だと、更新するかしないかの判断になると思いますが、勘違いでしょうか?

そうですよ
更新したいものを更新したいんですよね?更新するかしないか判断しないとそれはできませんよ

普通は更新したいデータだけ選んで更新するものです
更新パターンが複数あるなら複数回クエリを発行すればいいです

6
hatena 2023/12/05 (火) 21:22:16 修正

SQLでやろうとするので複雑になるのです。
Recordsetを開いてそれを更新すれば簡単です。

'tn: テーブル名
'd1: 開始時刻
'Sc: 抽出条件
Function kousin1(ByVal tn As String, ByVal d1 As String, ByVal Sc As String, ByRef adoCn As ADODB.Connection)
    Dim rs As New ADODB.Recordset
    rs.Open "SELECT * FROM " & tn & Sc, adoCn 
    If rs.EOF Then
        MsgBox "対象レコードが存在しませんでした。"
    Else
        Dim dt As Date
        dt = CDate(d1)
        If IsNull(rs!休憩開始) Then
            rs!休憩開始 = dt
        Else
            rs1!休憩開始2 = dt
        End If
        rs.Update
    End If
    rs.Close
End Function
7
タークン 2023/12/06 (水) 10:09:06 7a0a1@2705a

それは分かっているのですが、Recordsetで開きたくないのです。
Recordsetで開くと、データが多い場合はストレスを感じます。
いきなりデータが多いと難しいので、少ないデータでTRY中でした。
EXCELL VBAだけで、ACCESSを使っていなかった時は、
ストレスを通り越して動かなくなりました。
データはACCESSに登録してSQLでに移行して処理はできますが、
ストレスを感じるようになり、靴に小石が入った状態が続いています。
小石を取り除くために、可能な限りEXCELLを利用しない方法を探していたのです。
VBAかCを覚えろいう事になりますが、それは場違いな質問になってしまうので、
Recordsetで開いて処理するということで、解決とさせていただきます。
ありがとうございました。

9

それは分かっているのですが、Recordsetで開きたくないのです。
Recordsetで開くと、データが多い場合はストレスを感じます。

タイムカードの打刻を記録するとのことなので、レコード件数は1個だと思っていたのですが、
大量のレコードを更新する案件でしたか。

だとすると質問のSQLではすべて同じ時刻での更新になってしまうのでタイムカードのデータとしてはおかしな気がしますが。

もし、エクセルのデータをループで更新しようとしているなら、
レコードセットで1件ずつ更新するのと、
クエリで1件ずつ更新するのでは、
速度的には大差ないか、逆にクエリの方が遅くなります。

エクセルの大量のデータでクエリ(SQL)で更新する場合、高速化するなら、
エクセルのデータをAccessにリンクテーブル等で取り込んで、テーブル同志を連結して、
一気に更新するのが高速です。

10
すずやん 2023/12/06 (水) 12:03:41

すでに確認されていないかもしれませんが気になるので記載しておきます。

質問される場合は、入力と出力を明確に記載すれば、希望の回答がつきやすいです。
みなさん、回答に慣れておられるのでうまくされていますが、通常は突然「2023/12/15」とかが出てくると「?」となってしまいます。

あと、SQLやVBA にはそれぞれ得意の処理があると思います。
やろうとされている処理はおそらく?SQLでやるとVBAで実現する場合の何倍も複雑になると感じます。
そういったプログラムは概ね間違いやすく処理時間も長くなりやすいです。

11

まだ、見られているなら、ずすやんさんも言われてますが、
現状のエクセルシートのデータ例
Accessのテーブル構成、データ例
を提示していただければ、
速度の改善のアドバイスがつきやすいでしょう。

高速化の方法はいろいろありますが、データ処理によって適切な方法は異なりますので。

単純にRecordsetは遅いということはないです。
処理によってはRecordsetの方が速い場合もありますし、クエリする方か早い場合もあります。
場合によっては、エクセル側でデータベースとして扱いやすいように変換してから、という場合もあります。

12
タークン 2023/12/06 (水) 17:27:21 7a0a1@2705a

タイムカードなら、Recordsetで十分です。
簡単なので、むしろ有りだと思います。
他の用途のために、可能な限りシートに展開しないで、処理しようと思っているだけです。
Functionで配列を使用すると、いきなり重くなったりするので、
データを格納するというのも、どんなものなのか?
Pythonあたりに、ダイブしてみるのも有りかもです。

13

タイムカードなら、Recordsetで十分です。
簡単なので、むしろ有りだと思います。
他の用途のために、可能な限りシートに展開しないで、処理しようと思っているだけです。

あっ、別の話になっていたのですね。

Functionで配列を使用すると、いきなり重くなったりするので、

配列は高速化のための有用なツールです。
ツールの使い方を間違っているのかも。

今回はRecordsetで解決ということなので、これにて。

14
タークン 2023/12/07 (木) 10:30:11 7a0a1@2705a

ありがとうございます。
EXCELLのシートを利用しなければ、ほかの言語との処理速度に大差はないはずなので、
データを格納する方法を覚えつつ、申告対策としてPythonあたりを始めてみます。
実は、社会保険の電子申請する届出書作成アプリは、
EXCELLをCSVに変換したファイルだとUPできないという、
ほぼ不良品に近い欠点があるのです。
何十億もかけて始めた労働厚生省のアプリです。

100項目以上のヘーダーが無いCSVファイルを作るプログラムを3日かけて作ったあげく、
サポートダイアルの責任者から、使い物にならないという回答をいただきました。
直接、テキストに出力するしかないのです。
正直、怒りが収まりませんでした。

しかし、この問題を解決するには、
タイムカードを他の言語で作るしか無いので、
両方始めてみようと思います。

15
hiroton 2023/12/07 (木) 10:52:53 b2344@f966d

EXCEL(ECXEL VBA)もそんなに遅いモノじゃありません

「簡単に出来るがクッソ遅い処理」は簡単にできます。とにかく簡単にいろんなことができるソフトなので。速さが欲しいならそれなりに仕様を勉強する必要があります
こういうのとか

EXCELLをCSVに変換したファイルだとUPできない

EXCELからCSVを出力する方法もいろいろあります。「UPできないCSVの作り方」しか知らないだけですね
そもそも、CSVがなんたるか?を学べば、ただCSVと言っただけで常に互換が可能だという考えも知識不足だということがわかります。EXCELを問題にするのはお門違いでしょう

何をもってEXCELをこき下ろしてるのかわかりませんが、まぁ、Pythonが都合がよさそうだということであればそれでいいんじゃないですかね

あと、ここはACCESSの掲示板なので、EXCELでどうのこうの言いたいのなら別の場所を探したほうがいいと思いますよ

16
タークン 2023/12/09 (土) 16:33:22 7a0a1@2705a

EXCELLをこきおろしているわけではありません。
届書作成プログラムでは、
EXCELLからCSVファイルに変換したファイルは読み込めないと言っているのです。
これは、上席のサポートの人も、認めている事です。
「届書作成プログラムにUPでるCSVの作り方」をご存じのようなので、ぜひともご教授ください。
労働厚生省も、喜ぶはずです。

18

hirotonは「届書作成プログラム」に携わった人間ではありません。それが何を言っているのかもわかりません

CSV」がなんなのか学習してください。そのうえで「CSV出力に必要なもの」は質問者が提示してください


hirotonはこの場で誰かの文句に変わって仕事をするつもりなどありません

17

いろいろな話がごっちゃになってます。

最初の質問の「タイムカードデータの入力」はRecordsetで解決ということですよね。

そこから、派生的に下記のような流れになってます。

それは分かっているのですが、Recordsetで開きたくないのです。
Recordsetで開くと、データが多い場合はストレスを感じます。

処理の内容しだい。Recordsetの方が速い場合もあるし、SQLの方が速い場合もあります。

他の用途のために、可能な限りシートに展開しないで、処理しようと思っているだけです。

シートのセルを一つずつアクセスするような処理にするとたしかに重くなります。
パワークエリを使う、配列で処理する、Dictionaryオブジェクトを使う・・・など高速化の手段はいくらでもあります。

Functionで配列を使用すると、いきなり重くなったりするので、

どのような処理か不明ですが、使い方が悪いのだと思います。
正しく使えばたいていの処理は劇的にはやくなります。

実は、社会保険の電子申請する届出書作成アプリは、
EXCELLをCSVに変換したファイルだとUPできないという、
ほぼ不良品に近い欠点があるのです。
何十億もかけて始めた労働厚生省のアプリです。

おそらくそのアプリがCSVの標準から外れたものなのか、CSVへの変換設定に間違いがあるのか、・・・
Excelの欠点ではなく、そのアプリの欠陥ですよね。
どちらにしてもいいまでの流れからまったく関係ない話です。

100項目以上のヘーダーが無いCSVファイルを作るプログラムを3日かけて作ったあげく、

エクセルVBAで作ったということですか?

しかし、この問題を解決するには、
タイムカードを他の言語で作るしか無いので、

なぜ、このような結論になるのか分かりません。
エクセルVBAでも「100項目以上のヘーダーが無いCSVファイル」は可能です。
あるいはヘッダー付きで出力してあとからヘッダーのみ削除してもいいですね。

届書作成プログラムでは、
EXCELLからCSVファイルに変換したファイルは読み込めないと言っているのです。

エクセルのCSVファイルの出力は、CSVとしては標準的なものだと思いますので、
届書作成プログラムのフォーマットが特殊なのでは。
だとしたらVBAでテキスト出力プログラムを作成することは可能ですので、特殊な書式に合わせて出力することも可能です。

「届書作成プログラムにUPでるCSVの作り方」をご存じのようなので、ぜひともご教授ください。

そのCSVの仕様、出力サンプル、元となるエクセルデータなどを提示されたら回答はつくと思いますよ。

19
タークン 2023/12/11 (月) 16:08:40 7a0a1@2705a

届書作成プログラムのフォーマットが特殊なのでしょう。
OFFICEが利用できないアプリは、バグに近い仕様だと思います。
文字コードの仕様など、基礎的なことは確認済みです。
届書作成プログラムで吐き出したファイルを、
EXCELLに取り込んで、別名保存でCSVにした段階で、読み込めないファイルになるので、
原因は分からないが、EXCELL使ったらダメなんだなという判断は、正しいと思います。
もちろん、コードで処理しようが、手作業で別名で保存にしようが同じです。
テキストにした時点で、属性は無くなるはずなので、何か他の事が原因なのでしょう。
一番懸念しているのは、ACCESSのデータベースを使った時点でNGみたいなことが起きたら、
コスト的に自動化は放棄するしかないという事です。
すごく心配です。

いつ、電子申請が義務化されてもおかしくないので、
何とかしてほしいですが、行政に利用者の声など届きません。

20
すずやん 2023/12/11 (月) 17:45:12 >> 19

他の方々が疑問として提示されている内容や、アドバイスの内容と、それに対するタークンさんのコメントがかなり食い違っている感があります。
再度何に対してアドバイスされているのかをしっかり理解してみてください。

Excelでのファイルの出力方法は、「メニューのファイルから保存」するだけではありません。例えばExcelでVBAを使用することでCSVを出力できますが、これも試されましたか?
他、「届書作成プログラム」で出力したファイルに上書きする方法ではどうでしょうか?

Excelから出力したCSVファイルの16進コードや文字コードが「届書作成プログラム」で出力したファイルと比較して変わっていないかなどを確認するのも良いかもしれません。

※ちなみに「EXCELL」ではなく「Excel」です(「l」が多い)

21
タークン 2023/12/27 (水) 11:46:46 7a0a1@2705a

16進数の数字をCSV形式で保存してある物をエクセルで読み込もうとすると
"E"が指数表示で読み込まれるので、すぐにわかります。

文字コードは基礎的な事ことなので、最初からチェックしています。

EXCELで不具合が起きるという事は、
マイクロソフト系のソフトを使うと全滅の可能性があるのが怖いです。

まったく原因が分からないので、終了ということでありがとうございました。

22
すずやん 2023/12/27 (水) 13:40:42

16進コードとは「16進ダンプ」のことです。

23
hiroton 2023/12/27 (水) 14:52:24 1ade1@f966d

https://www.google.com/search?q=16進数の数字をCSV形式で保存してある物をエクセルで読み込もうとする

テキスト (.txt または .csv) ファイルのインポートまたはエクスポート

注: Excel で .csv ファイルを開くと、Excel は現在の既定のデータの書式設定を使用して、データの各列をインポートする方法を解釈します。 列を別のデータ形式により柔軟に変換する場合は、テキスト インポート ウィザードを使用することができます。 たとえば、.csv ファイルのデータ列の書式が MDY で、Excel の既定のデータ形式が YMD の場合、または先頭のゼロを含む数値の列をテキストに変換して先頭のゼロを維持できるようにする場合などがあります。 Excel でテキスト インポート ウィザードを強制的に実行するには、ファイル名の拡張子を .csv から .txt に変更してから開くか、テキスト ファイルに接続してインポートします (詳細については、次のセクションを参照してください)。

24
タークン 2023/12/29 (金) 19:43:33 7a0a1@2705a

社保のCSVファイルは、列数の異なるデータをひとつのファイルにまとめてあるため、
列数が少ない行には、不要なカンマが入ってしまう事が原因でした。
すべて、配列で読み込むとEXCELは止まってしまうため、
大物の個人データの範囲だけCSVで出力した後に、
データ量が少ない所属データを、先頭に書き込んで上書きすればエラーの出ないファイルができます。
電子申請したい方がいたら、ファイル名だけ変更すればそのまま使えるはずなので、コードをアップしておきます。

 Sub kansei()
 Dim fn As String, txt As String
 Dim strName() As String
 ReDim strName(1)

  '配列に値を入れる
  dat = ""
  With Sheets("sheet1")
   For i = 1 To 6
    dat = dat & .Cells(1, i) & ","
   Next
   dat = Left(dat, Len(dat) - 1) '最後のカンマを取り除く
   strName(0) = dat
   dat = ""
   For i = 1 To 11
    dat = dat & .Cells(4, i) & ","
   Next
   dat = Left(dat, Len(dat) - 1) '最終のカンマを取り除く
   strName(1) = dat
  End With

 fn = "C:\Users\tasuk\Desktop\A社保申告\SHFD0006temp - コピー.CSV"
 txt = CreateObject("Scripting.FileSystemObject").OpenTextFile(fn).ReadAll
  'Debug.Print strName(0) & vbCrLf & "[kanri]" & vbCrLf & ",001" & vbCrLf & strName(1)
  txt = strName(0) & vbCrLf & "[kanri]" & vbCrLf & ",001" & vbCrLf & strName(1) & vbCrLf & txt
  Open fn For Output As #1
     Print #1, txt
 Close #1

 End Sub

25

ご参考に。

Sub kansei2(fn As String)
    Dim txt As String
    Dim strName(1) As String
    With Sheets("sheet1")
        strName(0) = WorksheetFunction.TextJoin(",", True, .Cells(1, 1).Resize(, 6))
        strName(1) = WorksheetFunction.TextJoin(",", True, .Cells(4, 1).Resize(, 11))
    End With

    With CreateObject("Scripting.FileSystemObject").OpenTextFile(fn, 1)
        txt = .ReadAll
        .Close
    End With
    
    'Debug.Print strName(0) & vbCrLf & "[kanri]" & vbCrLf & ",001" & vbCrLf & strName(1)
    txt = strName(0) & vbCrLf & "[kanri]" & vbCrLf & ",001" & vbCrLf & strName(1) & vbCrLf & txt
    
    With CreateObject("Scripting.FileSystemObject").OpenTextFile(fn, 2)
        .Write txt
        .Close
    End With

End Sub

Sub Test()
    Call kansei2("C:\Users\tasuk\Desktop\A社保申告\SHFD0006temp - コピー.CSV")
End Sub
26
タークン 2023/12/30 (土) 17:02:37 7a0a1@2705a

CONCAT関数と、TEXTJOIN関数という新参者が現れたのですね。
確かに便利です。
数百程度のセルの処理だったら、いけそうな感じですね。
ありがとうございました。

にしてもです、社保のフォーマットが、所属部分を左端に持ってきていれば、
誰でも簡単素早く電子申請できるのですよ。