プログラミングBlog

(Node.js) sqlite3を使って大量のデータをInsert

概要

sqlite3を使って400件のデータをinsertする処理を行った後に、
DBに登録したデータの総件数を取得しましたが、
なぜか397件しか取得できていなかったため、理由をまとめました。

問題点について

問題点  

原因を調査していると他にも問題点がたくさんありました。

1. insertした時のデータが順序通りではなくばらばらになってしまっていた。
2. insertした総件数が正しい値で取得できていなかった。

3. autoincrementが上手く機能せずに空で登録されている
4. データをテキストファイルに書き込む処理にて、登録の順序がばらばら。

400件のinsert自体はうまくいっていたので上記4つを修正しました。

問題ありの修正部分

まず、非同期について考慮していなかったため、
順序通りの登録と、総件数の取得が上手くいっていませんでした。

問題点1と2については、insert部分の書き方insert処理が終了した後に総件数を取得するように修正しました。
動的なデータを登録する場合は、SQLインジェクションも考慮して、db.prepareを使う
insert処理終了後に、コールバックで総件数を取得するsqlを呼び出す。

修正前

// INSERT ALL
users.forEach((user, index) => {

    var insertSql = `insert or replace into user 
        (firstName, lastName, fullName)
        values (${user.firstName}, ${user.lastName}, ${user.fullName})`

    DbSetting.DbCommon.getDb().run(
        insertSql
    )

    fs.appendFile("insert.txt", insertSql, (error) => {
        if (error) throw err;
    });
})

// ALL COUNT
DbSetting.DbCommon.getDb().get(`select count(*) from user`, (err, row) => {
    if (err) {
        console.log(error)
    }
    console.log(row["count(*)"])
})

修正後

// db呼び出し
var db = DbSetting.DbCommon.getDb()

var insertSql = `insert into user 
        (firstName, lastName, fullName)
        values (? ,? ,?)`

var stmt = db.prepare(insertSql)

// INSERT ALL
users.forEach((user) => {
    stmt.run(`${user.firstName}`, `${user.lastName}`, `${user.fullName}`);

    fs.appendFileSync("insert.txt", insertSql, (err) => {
        if (err) throw err;
    });
})

stmt.finalize(() => {
    // ALL COUNT
    db.get(`select count(*) from user`, (err, row) => {
        if (err) {
            console.log(err)
        } else {
            console.log(row["count(*)"])
        }
    })
});

問題点3autoincrementoに関しては書き方を以下のように修正。
NUMBER型では使テーブルは作成されるが、autoincrementされないため、INTEGER型に修正。
NUMBER型だとROWIDとも紐づけが自動でされない模様。

id NUMBER AUTO INCREMENT PRIMARY KEY → userId INTEGER PRIMARY KEY AUTOINCREMENT

問題点4同期処理に変更
appendFile → appendFileSync

修正後の全体コード

dbCommon.js

var sqlite3 = require('sqlite3').verbose()

let db

exports.DbCommon = class DbCommon {
    static init() {
        db = new sqlite3.Database('sample.db');
    }

    static getDb() {
        return db
    }

    static initCreateTableUser() {
        db.serialize(() => {
            // CreateTableUser
            let CREATE_TABLE_USER = 'CREATE TABLE IF NOT EXISTS User'
                + '(userId INTEGER PRIMARY KEY AUTOINCREMENT,'
                + 'firstName TEXT NOT NULL,'
                + 'lastName TEXT NOT NULL,'
                + 'fullName TEXT NOT NULL)'

            db.run(CREATE_TABLE_USER, (error) => {
                if (error) {
                    console.log(error)
                }
                console.log(CREATE_TABLE_USER);
            });
        })
    }
}

index.js

const DbSetting = require('./dbCommon');
const UserModel = require('./User')

const fs = require('fs')

// CreateDataBase
DbSetting.DbCommon.init();

DbSetting.DbCommon.getDb();

// initCreateTableUser
DbSetting.DbCommon.initCreateTableUser()

let users = new Array()

// users
for (var i = 0; i < 400; i++) {
    users.push(new UserModel.User(i, i, i, i + i))
}

// db呼び出し
var db = DbSetting.DbCommon.getDb()

var insertSql = `insert into user 
        (firstName, lastName, fullName)
        values (? ,? ,?)`

var stmt = db.prepare(insertSql)

// INSERT ALL
users.forEach((user) => {

    stmt.run(`${user.firstName}`, `${user.lastName}`, `${user.fullName}`);

    fs.appendFileSync("insert.txt", `firstName:${user.firstName} lastName:${user.lastName} fullName:${user.fullName}\n`, (err) => {
        if (err) throw err;
    });
})

stmt.finalize(() => {
    // ALL COUNT
    db.get(`select count(*) from user`, (err, row) => {
        if (err) {
            console.log(err)
        } else {
            console.log(row["count(*)"])
        }
    })
});

GitHub

github.com

参考サイト

qiita.com

勉強会㉚

勉強会

開催日時 2021年7月17日(土) 7:00-9:00
本日は7:00-7:30まで研修会の内容
7:30-8:10,8:30-9:00もくもく。
Vue.jsを始めました。

本日の内容

発表者 テーマ 資料 時間
Nakagawa 研修について 30分

研修の話

今週の研修会で実装した内容。
疑似要素の話がでてきたので、知ってる知識を少し話す。
::が疑似要素
:の場合は疑似クラス
beforeやafterでcontentsを追加した場合、ドキュメントツリーに存在しないため、コピペができないなど。。。

Vue.js

JavaScriptフレームワーク
日本語版ドキュメント jp.vuejs.org

ドキュメントのサンプルを触ってみました。

コンポーネントの基本

var app = new Vue({
    el: '#app',
    data: {
        message: 1,
    }
})
    <div id="app">
        {{message}}
    </div>

グローバルに作成する場合

共通するコンポーネントを登録する場合の書き方

Vue.component('blog-post', {
    props: ['post'],
    template: '<div>'+
                        '<h3>{{post.title}}</h3>'+
                     '</div>'
});

var blog_post_demo = new Vue({
    el: "#blog_post_demo",
    data: {
        posts: [
            { id: 1, title: 'My journey'},
            { id: 2, title: 'My Vue'}
        ]
    }
});
    <div id="blog_post_demo">
        <blog-post 
        v-for="post in posts" 
        v-bind:id="post.id" 
        v-bind:post="post">
        </blog-post>
    </div>

勉強会㉙

Sharing meeting

開催日時 2021年7月10日(土) 7:00-9:00
本日共有会。
後半は各々開発。

本日の内容

発表者 テーマ 資料 時間
Nakagawa 研修について 15分
Toku 個人アプリ開発 15分

研修の話

今月から研修開始、主に自宅でVideoを見ながらのオンライン学習
Html,Css,Jqueryを中心に来週からJSPに入る予定。
意外と研修資料がしっかりしているイメージ。

個人的な感想

今後は案件に早い段階で入れるかが勝負だと思います。
未経験からだと周りの話を聞く限りではAWSLinuxの資格を持ってるのが前提条件として、 インフラの方が現場に入れてる人の話をよく聞く。
プログラミングはまだ厳しそう。
特にSESは即戦力をどうしても求められているので運良くはいれてもかなり苦労します。

個人アプリ

Activityについて。
Chart.jsを使用した、日と週のChart機能を実装中。
できたとこまでお披露目。
github.com

htmlのmeta要素

HTML5プロフェッショナル認定試験

HTML5プロフェッショナル認定試験に出てきそうなmeta要素をメモ程度にまとめる。
Web資格なら「HTML5プロフェッショナル認定試験」公式サイト

meta要素

  • meta要素とは・・・Webページに関する情報を埋め込むことができる。

    charset

    文字コードを指定する

<meta charset="UTF-8">

description

ページの説明を記述
検索された際にタイトル + 説明文が表示される。
こちらの説明文をきっちり書くと、クリック率が上がるらしい。

<meta name="description" content="meta要素のサンプルページ">

dafault-style

優先するスタイルシートの決定
以下の例だとfirstが優先的に指定される。

<meta http-equiv="default-style" content="first">
<link rel="stylesheet" type="text/css" href="sample.css" title="first">
<link rel="stylesheet" type="text/css" href="sample2.css" title="second">

reflesh

Webサイトが引っ越しした際の転送に使われる。
contentが何秒後に転送されるか指定。
urlが引っ越し先のURL。

<meta http-equiv="reflesh" content="3; url="sample2.html">

robots

検索エンジンクローラー)に対する設定ができる
allがdefault値

<meta name="robots" content="all">

content-security-policy(CSP)

セキュリティーポリシーを設定する
js, css, 画像などの読み込みをサイト自身のオリジンからのみ許可する設定。
XSS対策。

<meta http-equiv="Content-Security-Policy" content="default-src 'self'">

f:id:Tokuty:20210704214323p:plain

  • エラー出ましたが、インラインスクリプトを許可する設定script-src 'sha-256-xxxxxxxxxxxxxxxxxxx='を埋め込むとエラー解消されました。
<meta http-equiv="Content-Security-Policy" content=default-src 'sha256-xxxxxxxxxxxxxxxxxxx='">

viewport

<meta name="viewport" content="width=device-width, initial-scale=1.0">]

表示領域のこと。
このmeta要素なしでF12にてレスポンシブしてみると非常にわかりやすい。
画面のサイズに合わせて、表示領域を変更してくれる。
いろんなサイトを調べましたが、上記の設定値が最適解な模様。

全体のコード

sample.html

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="description" content="meta要素のサンプルページ">
    <meta http-equiv="default-style" content="first">
    <link rel="stylesheet" type="text/css" href="sample.css" title="first">
    <link rel="stylesheet" type="text/css" href="sample2.css" title="second">
    <meta name="robots" content="all">
    <meta http-equiv="Content-Security-Policy" content="default-src 'self';'">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <p class="sample.css">sample</p>
    <script>
        window.alert("hello world");
    </script>
    <script src="./sample.js"></script>
</body>

</html>

sample.css

p{
    color:red;
}

sample2.css

p{
    color:green;
}

sample.js

window.alert("hello world");

参考サイト

nature-company.com <meta name="robots">(robotsメタタグ)の正しい使い方と記述時のポイント | デジ研 qiita.com developers.google.com developer.mozilla.org

勉強会㉘

Hands-on and Lightning Talks

開催日時 2021年7月3日(土) 7:00-9:00
本日の発表者は一人!
後半は実務の話を少々。。

本日の内容

発表者 テーマ 資料 時間
Nakagawa メール機能・アカウント有効化実装中 30分

メール機能・アカウント有効化実装中

ruby on railsでgemを使ってメール機能作成
google gmailでの送信機能。
gemを使うと良い感じにやってくれるらしい。
本来ならsmtpClientを生成する→サーバー名とport番号を使ってメールサーバーに接続→ メールを作成→送信→メモリ開放、こんな感じの流れになるはず。。
それを簡単にやってのけていたのでgem恐るべし!

Ajax

Ajaxとは??

Asynchronous JavaScript Xml の略

同期通信であれば、クラインアント側の操作がサーバーからのレスポンスを待たなければできなかったが、 JavaScriptHTTP通信機能(XMLHttpRequest)を用いることで非同期(asynchronous)でサーバーと通信をすることによってサーバーからのレスポンスを待たずにクライアント側が操作できるようになった。
さらに非同期(asynchronous)でサーバーからデータを取得できるため、ページ全体ではなく、ページの一部だけ更新が可能となる。 その結果、サーバー間のトラフィック量も下がる。
e-words.jp

XMLHttpRequest

サーバーとの通信をする際に使うオブジェクト
今回はこちらのオブジェクトを使ってAjaxを実装していく。

GETの場合

やりたいこと

  1. xhr.openにてAPIからデータを取得。
  2. イベントハンドラーで取得したデータをinnerTextに追記していく。
  3. リクエストを送信する。

イベントハンドラ

通信の状態が変化した際に取得できるイベントハンドラ
onreadystatechangeを利用せずに以下のイベントでも可能

イベント名 意味
loadstart リクエスト送信
progress 送受信中
timeout タイムアウト
abort リクエスト中断
load リクエスト成功
error リクエスト失敗
loadend リクエスト完了

参考サイト
developer.mozilla.org

IPO Calendar API

今回はこちらのAPIを使用しました。
株式市場の上場スケジュールを取得できるAPI
ipo-cal.appspot.com

実装の説明

  • xhr.open() 
    第一引数にメソッド名、第二引数に取得したいデータが存在するURL
  • xhr.onreadystatechange 
    通信が成功した際のステータスコード(200)が返却された場合に取得したデータをinnerTextに追記する処理を記述。
  • xhr.addEventListener
    通信の状態が変化した際のイベントを登録。
    通信の流れが知りたいのですべて書いてみる。
  • xhr.send()
    リクエストを送信
  • innerText
    なぜinnerTextなのかはこちらを参考にしました。
    改行コードを考慮するので使用。
    qiita.com

GETの結果

f:id:Tokuty:20210627220222p:plain
データを取得できているようなので無事成功。
progress... → データ取得 → Success...  → Completed... の順番で通信が行われている模様。。

POSTの場合

実装の説明

  • xhr.setRequestHeader
    どんなデータを送信するかの設定を行います。
content-Type 種類
application/x-www-form-urlencoded テキスト
application/json Json
multipart/form-data バイナリ形式

POSTの結果

Post失敗時の画像 f:id:Tokuty:20210627215917p:plain

405エラーが返却されて、できませんでした。

f:id:Tokuty:20210627213345p:plain

ResponseのAllowにPostがないので、POSTはできないようです。

f:id:Tokuty:20210627213353p:plain

405エラーとは??

blog.hubspot.jp

全体のコード

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AjaxSample</title>
</head>

<body>
    <div>
        <labe>銘柄コード取得1:</labe>
        <input id="get_code1" type="text" value="">
        <button id="xhr_get_request1">Click_Get_Code1</button>
    </div>
    <div>
        <labe>銘柄コード取得2:</labe>
        <input id="get_code2" type="text" value="">
        <button id="xhr_get_request2">Click_Get_Code2</button>
    </div>

    <button id="xhr_post_request">Click_Post</button>
    <div>
        <p id="xhr_get_response1"></p>
    </div>
    <div>
        <p id="xhr_get_response2"></p>
    </div>
    <div>
        <p id="xhr_post_response"></p>
    </div>
    <script src="./sample.js"></script>
</body>

</html>
// Ajaxとは??
// Asynchronous JavaScript XML の略

// サーバー側と非同期通信を行い、受信結果をDOM経由でページに反映する仕組み
// get
const xhr_get_request1 = document.getElementById('xhr_get_request1');
const xhr_get_response1 = document.getElementById('xhr_get_response1');

const xhr_get_request2 = document.getElementById('xhr_get_request2');
const xhr_get_response2 = document.getElementById('xhr_get_response2');

// post
const xhr_post_request = document.getElementById('xhr_post_request');
const xhr_post_response = document.getElementById('xhr_post_response');

const get_code1 = document.getElementById('get_code1');
const get_code2 = document.getElementById('get_code2');

xhr_get_request1.addEventListener('click', function () {
    // 2秒後に実行
    setTimeout(xhr_request_method, 2 * 1000, 'http://ipo-cal.appspot.com/api/ipo/', xhr_get_response1, get_code1);
}, false);

xhr_get_request2.addEventListener('click', function () {
    // 5秒後に実行
    setTimeout(xhr_request_method, 5 * 1000, 'http://ipo-cal.appspot.com/api/ipo/', xhr_get_response2, get_code2);
});

// XMLHttpRequest
const xhr = new XMLHttpRequest();

// Ajax処理 get_request
function xhr_request_method(url, xhr_response, get_code) {
    xhr.open('GET', url + get_code.value);

    xhr.send(null);

    xhr_event(xhr, xhr_response, get_code.id);

    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                xhr_response.innerText += xhr.responseText + '\n';
            } else {
                xhr_response.innerText += 'failure!!\n';
            }
        }
    }
}

// Ajax処理 post_request
xhr_post_request.addEventListener('click', function () {
    xhr.open('POST', './sample.html');
    xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
    xhr.send("text");
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                xhr_post_response.innerText += xhr.responseText + '\n';
            } else {
                xhr_post_response.innerText += 'failure!!\n';
            }
        }
    }
});

// event
function xhr_event(_xhr, xhr_response, event_name) {
    // リクエスト送信をした時
    _xhr.addEventListener('loadstart', function () {
        xhr_response.innerText += `${event_name}:communication...\n`;
    });

    // データを送受信しているとき
    _xhr.addEventListener('progress', function () {
        xhr_response.innerText += `${event_name}:progress...\n`;
    });

    // タイムアウトした時
    _xhr.addEventListener('timeout', function () {
        xhr_response.innerText += `${event_name}:timeout...\n`;
    });

    // リクエストがキャンセルされた時
    _xhr.addEventListener('abort', function () {
        xhr_response.innerText += `${event_name}:abort...\n`;
    });

    // リクエストが成功した時
    _xhr.addEventListener('load', function () {
        xhr_response.innerText += `${event_name}:success...\n`;
    });

    // リクエストが失敗した時
    _xhr.addEventListener('error', function () {
        xhr_response.innerText += `${event_name}:error...\n`;
    });

    // リクエストが完了した時
    _xhr.addEventListener('loadend', function () {
        xhr_response.innerText += `${event_name}:completed...\n`;
    });
}

勉強会㉗

Hands-on and Lightning Talks

開催日時 2021年6月26日(土) 7:00-9:00
今週は共有会、最後にJsonのライブコーディングを行いました。

本日の内容

発表者 テーマ 資料 時間
Nakagawa 面談の話 30分
Kimi ロビー活動 20分
Inaba 退社の話 20分
Toku Jsonの話 Json 20分

面談の話

6件受けたが反応が良くない
どの案件も、何かしらのプロジェクト経験が必要
未経験だと現場に入れない
AWS経験をよく聞かれる

ロビー活動

固有名詞が多すぎるのでここには書けませんが、
主にIT関係の人たちの交流会の話。

退社の話

こちらもプライバシー保護のためここには書けませんが、
退社の話したと、これから転職活動をしてく行く話。

Jsonの話

Jsonデータ出力の例  

const fs = require('fs');
let jsonFile = fs.readFileSync('./sample.json');

// jsonからobjectに変換
let obj = JSON.parse(jsonFile)

// 扱えるデータ出力
console.log(obj);

// objectからjsonに変換
let json = JSON.stringify(obj, null, 4)

var data = { id: 1, value: 1 };

// json追記
fs.appendFileSync('./sample2.json', JSON.stringify(data) + "\n");

// 読込
let jsonFile2 = fs.readFileSync('./sample2.json', "utf8");

let arrayData = jsonFile2.split('\n');

// 結果
var result = JSON.parse(arrayData[0]);

Jsonの取り扱えるデータ

{
    "number": 1,
    "str": "str",
    "boolean": true,
    "null": null,
    "object": {},
    "array": []
}