現在の、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/15の休憩時間1のフィールドにデータが無かったら(NULLを含む)更新しないで、休憩時間2のフィールドにデータを登録する方法が知りたかったのです。
2023/12/15の休憩時間1のフィールドのデータの有無を調べるSQLを探してみたのですが、見つかりませんでした。フィールドが空だったら更新するが、フィールドにデータが入っていたら更新しないというのは、何かと利用する機会があるような気がするのです。
基本的には更新するデータとして抽出しないだけでしょう
複数のフィールドを同時に更新するのでレコードとして省きたくないのであれば、更新しない=元の値で更新するとすれば同じになるので
とかですかね
コードで記述するなら
Recordset
オブジェクトに取り込んでレコードをループ処理で処理したほうがいいと思いますがよく理解していないので、間違っていたら申し訳ありません。
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のセルには触れたくないと感じ始めたので、こだわっています。
そうですよ
更新したいものを更新したいんですよね?更新するかしないか判断しないとそれはできませんよ
普通は更新したいデータだけ選んで更新するものです
更新パターンが複数あるなら複数回クエリを発行すればいいです
SQLでやろうとするので複雑になるのです。
Recordsetを開いてそれを更新すれば簡単です。
それは分かっているのですが、Recordsetで開きたくないのです。
Recordsetで開くと、データが多い場合はストレスを感じます。
いきなりデータが多いと難しいので、少ないデータでTRY中でした。
EXCELL VBAだけで、ACCESSを使っていなかった時は、
ストレスを通り越して動かなくなりました。
データはACCESSに登録してSQLでに移行して処理はできますが、
ストレスを感じるようになり、靴に小石が入った状態が続いています。
小石を取り除くために、可能な限りEXCELLを利用しない方法を探していたのです。
VBAかCを覚えろいう事になりますが、それは場違いな質問になってしまうので、
Recordsetで開いて処理するということで、解決とさせていただきます。
ありがとうございました。
タイムカードの打刻を記録するとのことなので、レコード件数は1個だと思っていたのですが、
大量のレコードを更新する案件でしたか。
だとすると質問のSQLではすべて同じ時刻での更新になってしまうのでタイムカードのデータとしてはおかしな気がしますが。
もし、エクセルのデータをループで更新しようとしているなら、
レコードセットで1件ずつ更新するのと、
クエリで1件ずつ更新するのでは、
速度的には大差ないか、逆にクエリの方が遅くなります。
エクセルの大量のデータでクエリ(SQL)で更新する場合、高速化するなら、
エクセルのデータをAccessにリンクテーブル等で取り込んで、テーブル同志を連結して、
一気に更新するのが高速です。
すでに確認されていないかもしれませんが気になるので記載しておきます。
質問される場合は、入力と出力を明確に記載すれば、希望の回答がつきやすいです。
みなさん、回答に慣れておられるのでうまくされていますが、通常は突然「2023/12/15」とかが出てくると「?」となってしまいます。
あと、SQLやVBA にはそれぞれ得意の処理があると思います。
やろうとされている処理はおそらく?SQLでやるとVBAで実現する場合の何倍も複雑になると感じます。
そういったプログラムは概ね間違いやすく処理時間も長くなりやすいです。
まだ、見られているなら、ずすやんさんも言われてますが、
現状のエクセルシートのデータ例
Accessのテーブル構成、データ例
を提示していただければ、
速度の改善のアドバイスがつきやすいでしょう。
高速化の方法はいろいろありますが、データ処理によって適切な方法は異なりますので。
単純にRecordsetは遅いということはないです。
処理によってはRecordsetの方が速い場合もありますし、クエリする方か早い場合もあります。
場合によっては、エクセル側でデータベースとして扱いやすいように変換してから、という場合もあります。
タイムカードなら、Recordsetで十分です。
簡単なので、むしろ有りだと思います。
他の用途のために、可能な限りシートに展開しないで、処理しようと思っているだけです。
Functionで配列を使用すると、いきなり重くなったりするので、
データを格納するというのも、どんなものなのか?
Pythonあたりに、ダイブしてみるのも有りかもです。
あっ、別の話になっていたのですね。
配列は高速化のための有用なツールです。
ツールの使い方を間違っているのかも。
今回はRecordsetで解決ということなので、これにて。
ありがとうございます。
EXCELLのシートを利用しなければ、ほかの言語との処理速度に大差はないはずなので、
データを格納する方法を覚えつつ、申告対策としてPythonあたりを始めてみます。
実は、社会保険の電子申請する届出書作成アプリは、
EXCELLをCSVに変換したファイルだとUPできないという、
ほぼ不良品に近い欠点があるのです。
何十億もかけて始めた労働厚生省のアプリです。
100項目以上のヘーダーが無いCSVファイルを作るプログラムを3日かけて作ったあげく、
サポートダイアルの責任者から、使い物にならないという回答をいただきました。
直接、テキストに出力するしかないのです。
正直、怒りが収まりませんでした。
しかし、この問題を解決するには、
タイムカードを他の言語で作るしか無いので、
両方始めてみようと思います。
EXCEL(ECXEL VBA)もそんなに遅いモノじゃありません
「簡単に出来るがクッソ遅い処理」は簡単にできます。とにかく簡単にいろんなことができるソフトなので。速さが欲しいならそれなりに仕様を勉強する必要があります
こういうのとか
EXCELからCSVを出力する方法もいろいろあります。「UPできないCSVの作り方」しか知らないだけですね
そもそも、CSVがなんたるか?を学べば、ただCSVと言っただけで常に互換が可能だという考えも知識不足だということがわかります。EXCELを問題にするのはお門違いでしょう
何をもってEXCELをこき下ろしてるのかわかりませんが、まぁ、Pythonが都合がよさそうだということであればそれでいいんじゃないですかね
あと、ここはACCESSの掲示板なので、EXCELでどうのこうの言いたいのなら別の場所を探したほうがいいと思いますよ
EXCELLをこきおろしているわけではありません。
届書作成プログラムでは、
EXCELLからCSVファイルに変換したファイルは読み込めないと言っているのです。
これは、上席のサポートの人も、認めている事です。
「届書作成プログラムにUPでるCSVの作り方」をご存じのようなので、ぜひともご教授ください。
労働厚生省も、喜ぶはずです。
hirotonは「届書作成プログラム」に携わった人間ではありません。それが何を言っているのかもわかりません
「CSV」がなんなのか学習してください。そのうえで「CSV出力に必要なもの」は質問者が提示してください
hirotonはこの場で誰かの文句に変わって仕事をするつもりなどありません
いろいろな話がごっちゃになってます。
最初の質問の「タイムカードデータの入力」はRecordsetで解決ということですよね。
そこから、派生的に下記のような流れになってます。
処理の内容しだい。Recordsetの方が速い場合もあるし、SQLの方が速い場合もあります。
シートのセルを一つずつアクセスするような処理にするとたしかに重くなります。
パワークエリを使う、配列で処理する、Dictionaryオブジェクトを使う・・・など高速化の手段はいくらでもあります。
どのような処理か不明ですが、使い方が悪いのだと思います。
正しく使えばたいていの処理は劇的にはやくなります。
おそらくそのアプリがCSVの標準から外れたものなのか、CSVへの変換設定に間違いがあるのか、・・・
Excelの欠点ではなく、そのアプリの欠陥ですよね。
どちらにしてもいいまでの流れからまったく関係ない話です。
エクセルVBAで作ったということですか?
なぜ、このような結論になるのか分かりません。
エクセルVBAでも「100項目以上のヘーダーが無いCSVファイル」は可能です。
あるいはヘッダー付きで出力してあとからヘッダーのみ削除してもいいですね。
エクセルのCSVファイルの出力は、CSVとしては標準的なものだと思いますので、
届書作成プログラムのフォーマットが特殊なのでは。
だとしたらVBAでテキスト出力プログラムを作成することは可能ですので、特殊な書式に合わせて出力することも可能です。
そのCSVの仕様、出力サンプル、元となるエクセルデータなどを提示されたら回答はつくと思いますよ。
届書作成プログラムのフォーマットが特殊なのでしょう。
OFFICEが利用できないアプリは、バグに近い仕様だと思います。
文字コードの仕様など、基礎的なことは確認済みです。
届書作成プログラムで吐き出したファイルを、
EXCELLに取り込んで、別名保存でCSVにした段階で、読み込めないファイルになるので、
原因は分からないが、EXCELL使ったらダメなんだなという判断は、正しいと思います。
もちろん、コードで処理しようが、手作業で別名で保存にしようが同じです。
テキストにした時点で、属性は無くなるはずなので、何か他の事が原因なのでしょう。
一番懸念しているのは、ACCESSのデータベースを使った時点でNGみたいなことが起きたら、
コスト的に自動化は放棄するしかないという事です。
すごく心配です。
いつ、電子申請が義務化されてもおかしくないので、
何とかしてほしいですが、行政に利用者の声など届きません。
他の方々が疑問として提示されている内容や、アドバイスの内容と、それに対するタークンさんのコメントがかなり食い違っている感があります。
再度何に対してアドバイスされているのかをしっかり理解してみてください。
Excelでのファイルの出力方法は、「メニューのファイルから保存」するだけではありません。例えばExcelでVBAを使用することでCSVを出力できますが、これも試されましたか?
他、「届書作成プログラム」で出力したファイルに上書きする方法ではどうでしょうか?
Excelから出力したCSVファイルの16進コードや文字コードが「届書作成プログラム」で出力したファイルと比較して変わっていないかなどを確認するのも良いかもしれません。
※ちなみに「EXCELL」ではなく「Excel」です(「l」が多い)
16進数の数字をCSV形式で保存してある物をエクセルで読み込もうとすると
"E"が指数表示で読み込まれるので、すぐにわかります。
文字コードは基礎的な事ことなので、最初からチェックしています。
EXCELで不具合が起きるという事は、
マイクロソフト系のソフトを使うと全滅の可能性があるのが怖いです。
まったく原因が分からないので、終了ということでありがとうございました。
16進コードとは「16進ダンプ」のことです。
https://www.google.com/search?q=16進数の数字をCSV形式で保存してある物をエクセルで読み込もうとする
テキスト (.txt または .csv) ファイルのインポートまたはエクスポート
社保の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
ご参考に。
CONCAT関数と、TEXTJOIN関数という新参者が現れたのですね。
確かに便利です。
数百程度のセルの処理だったら、いけそうな感じですね。
ありがとうございました。
にしてもです、社保のフォーマットが、所属部分を左端に持ってきていれば、
誰でも簡単素早く電子申請できるのですよ。