JS 实现CSV文件转换SQL文件小工具

news2024/11/15 21:30:51

一. 需求

最近在项目中遇到一个问题,客户提供的数据是CSV格式的,
需要将CSV文件中的数据转换为SQL语句文件。

😅由于本人不会Excel的vba编程,因此决定使用JS来实现。


二. 实现思路

  • 提供一个文件上传框,支持多文件上传,且只能上传csv文件
  • 使用new FileReader()来读取上传的csv文件
  • 每个csv文件都包含标题栏,因此需要从第二行开始读取数据
  • 将读取到的数据构造成insert的sql语句然后存放到一个list中
  • 创建Blob对象,将包含sql的list放入该对象中构建sql文本对象
  • 通过URL.createObjectURL()获取Blob对象在内存中的地址
  • 创建a标签,配合内存中的地址实现sql文件下载

三. CSV文件例子

"CQ企画番号","共同購入商品コード","CQ商品コード","JANコード","定催区分","部門コード","大分類","中分類","小分類","集品区分","商品名漢字","規格名漢字","入数","納品時容器区分","シール貼付有無区分","資材名","税抜組合員単価","税込組合員単価","企画年月回","配達年月回","企画単協 エフ","企画単協 さが","企画単協 ララ","企画単協 おおいた","企画単協 水光社","企画単協 みやざき","企画単協 かごしま","企画単協 おきなわ","税コード","消費税率","単価計算区分","税抜計算区分","税込計算区分","禁則チェック区分","エラーコード","削除フラグ","作成日","作成時刻","更新日","更新時刻"
"301","1005405","100317290","2000000432908","1","02","01","02","09","02","フレンドリーバナナ(フィリピン産)","750g",1,"2","0",,288,311,"2023041","       ","0","0","0","0","0","0","0","1","080",0.08,"2","2","1","1","  ","0","20230117","100742","20230309","102624"
"301","1039130","100499798","2000000648941","1","02","01","02","09","02","フレンドリーバナナ(フィリピン産)","500g(3?5本)",1,"2","0",,278,300,"2023041","       ","0","0","0","0","0","0","0","1","080",0.08,"2","2","1","1","  ","0","20230117","100742","20230309","102624"
"301","1355180","800022912","2008000229122","1","01","01","01","02","02","貝割大根(大分県産)","1パック",20,"2","0",,36,38,"2023041","       ","0","0","0","1","0","0","0","0","080",0.08,"2","2","1","1","  ","0","20230117","100742","20230309","102624"
"301","1395165","800055699","2000000678788","1","01","01","00","05","02","たまねぎ(北海道産)","300g(大小混)",1,,"0",,138,149,"2023041","       ","0","0","0","0","0","0","0","1","080",0.08,"2","2","1","1","  ","0","20230117","100742","20230309","102624"
"301","1409182","100433518","2000000601557","2","01","01","00","05","00","よくねたいも〈北あかり〉(北海道産)","500g(大小混)",1,"1","0",,258,278,"2023041","       ","0","0","0","0","0","0","0","1","080",0.08,"2","2","1","1","  ","0","20230302","130441","20230309","102458"
"301","1417550","800072337","2008000723378","1","01","01","00","01","05","ほうれん草(長崎県産)","150g",1,"2","0",,138,149,"2023041","       ","0","0","1","0","0","0","0","0","080",0.08,"2","2","1","1","  ","0","20230117","100742","20230309","102624"

四. 转换工具

  • 复制代码创建html文件,拿来就用。😋不用配置环境
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSV转换SQL工具</title>
</head>
<body>
    <div style="display: flex;">
        <label for="schema">指定schema</label>
        <select id="schema">
            <option value=""></option>
            <option value="nosan">nosan 农业</option>
            <option value="sozai">sozai 蔬菜</option>
            <option value="shokan">shokan 商管</option>
        </select>
    </div>
    <hr>

    <label for="removeBlank">是否去除字符串中的空白</label>
    <input id="removeBlank" type="checkbox" />
    <hr>

    <label for="csv">请上传csv文件</label>
    <input id="csv" type="file" accept=".csv" multiple />
    <hr>

    <button id="transform">csv转sql开始</button>
    <hr>

    <!-- 显示错误消息的区域 -->
    <div id="error" style="color: red;">
        
    </div>
</body>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script>
    // 是否去除字符串空白flag
    let removeBlank = false;

    $(function() {
        transformEvent();
    });

    function transformEvent() {

        $("#transform").click(function() {

            removeBlank = $("#removeBlank").prop("checked");

            const schema = $("#schema").val();
            if(!schema) {
                alert("请指定schema!");
                return;
            }

            // 清空所有的内容
            $("#error").empty();

            // 获取上传的所有csv文件
            const csvFileList = $("#csv").get(0).files;
            for(csvfile of csvFileList) {

                const sqlList = [];

                /*
                    将上传的文件名称当做表名
                    注意:
                        下面这行代码不能放到reader的load回调里面,
                        否则生成的csv的文件名会相同
                */
                const tableName = csvfile.name.split(".")[0];
                
                // 创建csv文件读取对象
                const reader  = new FileReader();

                // 指定要读取的CSV的编码
                reader.readAsText(csvfile, "Shift_JIS");
                reader.addEventListener('load', ({target: {result: csvText}}) => {

                    // 获取出标题栏之外的csv内容数据
                    const hanleCSVData = csvText.slice(csvText.indexOf('\n') + 1).split('\n');

                    // 如果上传的csv文件只有标题没有数据
                    if(!hanleCSVData || hanleCSVData.length === 1 && !hanleCSVData[0].trim()) {
                        const newHtml = $("#error").html() + "<br />" + tableName + ".csv文件没有内容!";
                        $("#error").html(newHtml);
                        return;
                    }

                    for (const csvRowData of hanleCSVData) {

                        if (!csvRowData) {
                            continue;
                        }

                        // 构造插入SQL
                        let insertSql = SqlUtils.createBaseSql(schema, tableName);
                        
                        // 创建要插入sql的值
                        const sqlValueStr = SqlUtils.addCsvRowToSqlItem(csvRowData);
                        
                        // 补足自定义项目后,构建
                        const insertSqlStr = SqlUtils.customeSqlHandle(insertSql + sqlValueStr);

                        sqlList.push(insertSqlStr);
                    }

                    // 下载转换之后的sql文件
                    fileDownload(sqlList, `${tableName}.sql`);
                });
            }
        });
    };

    class SqlUtils {

        // 获取当前时间 yyyy/MM/dd HH:mm:ss
        static nowDate = new Date().toLocaleString();

        // 是否去除字符串中的空白
        static trimStrFlg = true;

        // 创建基本的插入SQL语句
        static createBaseSql(schema, tableName) {
            // 因为csv项目和表项目完全一致,因此可以省略项目
            return `INSERT INTO [${schema}].${tableName} VALUES (`;
        }

        static addCsvRowToSqlItem(csvRowData) {

            let sqlItem = "";
            // 获取CSV所有的列
            const csvItemList = csvRowData.split(",");
            const newcsvItemList = [];

            // 对csv项目进行特殊处理
            for (const csvItem of csvItemList) {
                if(!!csvItem) {
                    /*
                        如果需要去除空格,并且是csv中的字符串项目的话
                    */
                    if (removeBlank && csvItem.includes("\"")) {
                        // "测试     "   ===> '测试'
                        newcsvItemList.push("\'" + csvItem.replaceAll('"', '').trim() + "\'");
                    } else {
                        // 插入的是数字
                        newcsvItemList.push(csvItem);
                    }

                } else {
                    // 如果csv项目为空项目则插入NULL
                    newcsvItemList.push('NULL');
                }
            }

            // 将list转换为以 , 分隔的字符串,并把 " 替换为 ' ,因为sql插入时只能用 '
            return newcsvItemList.join(", ").replaceAll('\"', '\'') + ", ";
        }

        static customeSqlHandle(insertSql) {
            // 插入时间
            const SKSI_NCHJ = `'${SqlUtils.nowDate}'`;
            // 更新时间
            const KUSHN_NCHJ = `'${SqlUtils.nowDate}'`;

            // 添加换行符
            return `${insertSql}${SKSI_NCHJ}, ${KUSHN_NCHJ});\r\n`;
        }
    }

    function fileDownload(data, fileName) {

        // 创建文件下载的url
        const insertSqlStrBlob = new Blob(data, {type: "application/sql"});
        const src = URL.createObjectURL(insertSqlStrBlob);

        // 创建a标签
        const aElement = document.createElement('a');
        aElement.download = fileName;
        aElement.style.display = 'none';
        aElement.href = src;

        // 将a标签添加到页面上手动触发点击事件,从而触发文件下载
        document.body.appendChild(aElement);
        aElement.click();
        document.body.removeChild(aElement);

        URL.revokeObjectURL(src);
    }
</script>
</html>

五. 效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/731010.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序全局配置详解

通过全局配置实现的效果 开发者 微信公众平台 登录网址 https://mp.weixin.qq.com/ 注册网址 https://mp.weixin.qq.com/cgi-bin/wx?token&langzh_CN 微信小程序开发者工具 下载地址 https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 小…

WPF grid控件定义行和列

在此已经学习了wpf Grid控件&#xff0c; WPF布局控件Grid的基本使用 - 使用kaxaml_bcbobo21cn的博客-CSDN博客 下面继续学习&#xff1b; 定义3行3列的基本代码如下&#xff1b;为了看清效果&#xff0c;设置 ShowGridLines"True"&#xff1b; <Grid ShowGrid…

Tkinter_鼠标选中样式

前言 使用tkinter库创建窗口应用程序示例&#xff0c;包含不同鼠标样式标签。 一、方法 import tkinter as tkclass Mouse_Style:def __init__(self):self.root tk.Tk()self.root.title(样式)self.root.geometry("200x5201100150")self.interface()def interface(…

【G431】多路ADC+DMA采集

文章目录 前言1.CubeMx的配置步骤3.测试代码4.演示效果 总结 前言 之前蓝桥杯程序中使用的是查询方式的ADC&#xff0c;缺点是当采集通道多的时候会导致CPU速率变低从而导致查询ADC代码之后的程序会被阻塞。而DMA就不会&#xff0c;DMA会绕开CPU直接传输。 1.CubeMx的配置步骤…

GDB 查看、修改变量

1、info args : 查看函数的入参内容 2、set print null-stop :遇到无效的字符就不显示出来了 3、set print pretty :让结构体以定义的形式展示出来&#xff0c;一个字段一行 4、 set print arry on :让数组中的元素每个占一行显示 5、 p sizeof(xxx) : 查看xxx的大小&#xff…

STM32 DSP库CUBEMX配置+FFT频率计算

文章目录 前言一、DSP库添加1.1 加一个define1.2 添加文件路径1.3 主函数 二、FFT运算求频率2.1 初始版本版本2 总结 前言 使用DSP中的函数加快计算。 本文首先讲述如何通过添加dsp库。 再讲述使用DSP库进行实数FFT运算。&#xff08;FFT运算用到了前面讲述的STM32CubeMX-ADC …

mysql 安装

常用存储引擎功能对比&#xff1a; mysql 安装流程&#xff1a; 如果直接本地安装&#xff0c;就只需要安装好然后配置环境变量就行了&#xff0c;my.ini文件会自动帮你写好&#xff0c;如果是zip压缩包安装就需要自己写my.ini文件&#xff0c;下面详细介绍zip压缩包安装 下载…

提示工程的前世今生

原文链接&#xff1a;芝士AI吃鱼 通过提示进行情境学习 在生物学中&#xff0c;涌现是一种令人难以置信的特性&#xff0c;由于相互作用的结果&#xff0c;各个部分聚集在一起&#xff0c;表现出新的行为&#xff08;称为涌现&#xff09;&#xff0c;这是你在较小的尺度上看不…

分别基于红黑树、timefd、多级时间轮实现定时器

文章目录 一、定时器的应用二、定时器的触发方式2.1 网络事件和定时事件在一个线程中处理2.2 二、定时器的设计2.1 接口设计2.2 数据结构设计2.2.1 红黑树2.2.3 最小堆2.2.4 时间轮 三、利用红黑树实现定时器3.1 数据结构3.2 接口实现3.2.1 初始化定时器3.2.2 添加定时器3.2.3 …

【CPU】关于x86、x86_64/x64、amd64和arm64/aarch64

为什么叫x86和x86_64和AMD64? 为什么大家叫x86为32位系统&#xff1f; 为什么软件版本会注明 for amd64版本&#xff0c;不是intel64呢&#xff1f; x86是指intel的开发的一种32位指令集&#xff0c;从386开始时代开始的&#xff0c;一直沿用至今&#xff0c;是一种cisc指令…

Github 标星68.5K,不愧是阿里P8架构师整理的 Java 面试复盘笔记

Java 面试 2021 已经过半&#xff0c;不知道大家在今年的金三银四是否拿到了自己理想的 Offer&#xff1f;大家的技术面一共面了多少轮&#xff1f;作为一名程序员&#xff0c;技术面试是不可避免的一个环节&#xff0c;一般技术面试官都会通过自己的方式去考察程序员的技术功…

Java性能诊断利器:arthas详细教程

目录 1.概述 2.使用 2.1.安装使用 2.2.启动 2.3.全局监控 2.4.方法级别的监控 2.4.1.插件 2.4.2.watch 2.3.3.trace 2.3.4.stack 2.4.线上问题定位 2.4.1.反编译 2.4.2.CPU占用率 2.4.3.死锁 1.概述 arthas是Alibaba推出的一款JVM性能诊断调优工具&#xff0c;主…

C++之代理模式

目录 模式思想 简介 组成 优点 代码实现 情景 如果不使用代理的话&#xff1a; 加代理的话&#xff1a; 结果 模式思想 简介 代理模式&#xff1a; 通过代理类&#xff0c;来控制实际对象的访问权限。 在某些情况下&#xff0c;一个对象不适合或者不能直接引用另…

深入理解java虚拟机精华总结:硬件的效率与一致性、Java内存模型、Java与线程、Java与协程

深入理解java虚拟机精华总结&#xff1a;硬件的效率与一致性、Java内存模型、Java与线程、Java与协程 硬件的效率与一致性Java内存模型主内存与工作内存内存间交互操作对于volatile型变量的特殊规则针对long和double型变量的特殊规则原子性、可见性与有序性原子性可见性有序性 …

数学建模之灰色预测方法

数学建模之灰色预测方法 目录灰色关联度矩阵关联系数关联矩阵 GM(1,1)模型相关原理matlab程序 目录 灰色关联度矩阵 关联系数 计算关联系数公式 步骤 第一步 第二步 求参考数列和待比较数列矩阵数值做差之后的最小值和最大值 第三步利用公式计算关联度系数&#xff0c;其中将…

【C++修炼之路】32.智能指针

每一个不曾起舞的日子都是对生命的辜负 智能指针 一.为什么需要智能指针&#xff1f;二.智能指针解决new抛异常的示例三.智能指针的使用及其原理3.1 RAII3.2 SmartPtr存在的问题3.3 std::auto_ptr3.4 std::unique_ptr3.5 std::shared_ptrstd::shared_ptr的基本设计std::shared_…

海明码(汉明码)原理及其计算方法

海明码&#xff08;汉明码&#xff09;是一种利用奇偶性检测和纠正错误的编码方法。在传输和储存数据时&#xff0c;可能会发生传输错误或数据损坏。海明码通过在数据中添加冗余位来检测错误并进行纠正&#xff0c;提高了传输和存储数据的可靠性。 基本原理&#xff1a;将原始…

【数据结构复习】汉诺塔:快速回忆汉诺塔问题

题目 题解 众所周知这是一个递归问题&#xff08; 我们只需要注意两点&#xff1a;①什么时候退出递归 ②怎么从n-1推出n ①什么时候退出递归&#xff1a;很明显啦&#xff0c;n1的时候&#xff0c;我们直接把A位置的唯一盘子移到C上&#xff0c;大功告成。 ②怎么从n-1推出n…

Vision Pro销售策略曝光,面罩/头带/屈光镜片加大零售难度

彭博社Mark Gurman再次发布了关于苹果Vision Pro的销售策略&#xff0c;以及零售方面的难题。 一、销售计划和策略 1&#xff0c;2024年初先在美国部分门店销售&#xff0c;仅线下购买&#xff0c;线上暂不开放。购买方式是先线上预约&#xff08;可能要提供面部扫描图、眼镜…

Python 利用opencv识别某象旋转验证码,识别率达95%以上

本期介绍某象旋转验证码识别,识别的思想其实与上篇文章识别滑动还原验证码相似,也是借鉴过来的,但是旋转验证码更加复杂,实现起来稍加困难,下面来看一下,原始数据集和识别之后数据集。 原始数据集: 将圆图旋转成功之后的数据集: 注意:我这里仅仅抓取了几十张作为数据…