Code Monkey home page Code Monkey logo

sql-mother's Introduction

SQL 之母 - 闯关式 SQL 自学网

纯前端实现的闯关式 SQL 自学网

By 程序员鱼皮 ,一人全役

在线体验:http://sqlmother.yupi.icu

视频演示:https://www.bilibili.com/video/BV1pV4y1i7LW

项目介绍

一个完全免费的闯关式 SQL 自学教程网站,结合鱼皮自己的 SQL 学习实践经验,编写了 30 多个关卡,用户可以在线提交 SQL 代码做题闯关,目标是从 0 到 1 地带大家掌握常用的 SQL 语法。

此外,网站支持自由选择关卡、自定义关卡、SQL 在线练习广场等功能。

为什么做这样一个网站?

首先,SQL 知识极为重要,几乎是程序员、产品经理、数据分析同学的必备技能。

对于 SQL 的学习,比起看教程,更适合通过实战来入门。网上虽然也有类似的 SQL 自学网,但是要么收费、要么不够体系化。

所以鱼皮决定自己动手,搞一个开源的 SQL 学习网,一方面希望能够帮助大家更轻松地入门 SQL;另一方面,也希望项目代码也能给大家一些启发,让更多同学有机会参与进来成为贡献者,一起做好一个项目!

20 秒学会使用

1)直接进入主页,左侧是教程和题目区域,请先完整阅读

2)在右上区域编写 SQL 代码做题,点击运行提交结果

3)可以通过右下的题目助手区域帮助自己做题

4)执行结果正确后,可以进入下一关

SQL 之母使用教程

你也可以自由选择关卡来挑战,所有关卡都没有任何限制,不一定非要按顺序做题:

选择关卡

1 分钟本地启动

由于项目采用纯前端实现,本地启动项目非常简单!

在线访问人数较多,可能会卡顿,所以更推荐大家自己在本地使用~

1)下载本项目代码

2)进入项目根目录,执行 npm install 安装项目依赖

3)执行 npm run dev 本地启动即可

功能和特性

  • 展示教程题目文档(Markdown 格式)
  • 在线做题
    • 比对结果
    • 题目助手
      • 展示执行结果
      • 查看提示
      • 查看建表语句
      • 查看答案
  • 关卡设置
    • 自由选择关卡
    • 主线关卡 - 支持上一关 / 下一关
    • 自定义关卡
  • SQL 广场(自由输入 SQL)

SQL 广场

技术选型

本项目采用纯前端实现,不需要任何后端的前置知识~

Q:为什么采用纯前端实现?

A:减少攻击风险 + 省钱 + 新的学习尝试

  • 主框架:Vue 3
  • 组件库:ant-design-vue
  • Markdown 展示组件:bytemd + github-markdown-css 主题
  • 代码编辑器:monaco-editor
  • SQL 执行:sql.js
  • SQL 代码格式化:sql-formatter
  • 全局状态管理:pinia + pinia-plugin-persistedstate
  • 前端工程化:typescript + eslint + prettier
  • 工具库:lodash

核心设计

1、界面模块化

采用模块化的开发**,把做题页面(主页)拆分为题目浏览区、SQL 编码区、题目结果区,每个区都是一个独立的 Vue 组件文件,实现了逻辑的隔离和组件的复用(比如 SQL 编码区同样可以复用到 SQL 练习广场页面)。

  • 题目浏览区(QuestionBoard):展示题目 Markdown 文档
  • SQL 编码区(SqlEditor):封装了代码编辑器、运行 / 格式化 / 重置按钮
  • 题目结果区(SqlResult):封装了题目执行结果的展示

然后在 IndexPage.vue 中就可以引入这些组件,并且传递关卡信息、运行结果等数据给组件,组装成一个完整的页面。

2、关卡设计

虽然没有后端数据库,但是仍应该把所有关卡的数据统一进行管理,所以定义了 levels 目录,统一存放关卡相关数据。

首先将关卡分为了两类,主线关卡(教程)和自定义关卡(便于扩展),分别在 mainLevels.tscustomLevels.ts 文件中进行管理。

每个关卡都是一个单独的目录,实现了关卡之间的隔离。

每个关卡独立目录

由于每个关卡的题目教程文章可能非常长,直接写在 ts 文件中不利于阅读和管理,所以这里的策略是把所有文章写在 .md Markdown 文件中,在关卡定义文件 index.ts 中读取 .md 文件。

示例代码如下,每个关卡的信息独立定义、相互隔离:

import md from "./README.md?raw";
import sql from "./createTable.sql?raw";

export default {
  key: "level1",
  title: "基础语法 - 查询 - 全表查询",
  initSQL: sql,
  content: md,
  defaultSQL: "select * from student",
  answer: "select * from student",
  hint: "请仔细查看本关给出的示例",
  type: "main",
} as LevelType;

3、纯前端 SQL 执行

纯前端是怎么操作数据库、执行 SQL 的呢?有前端经验的同学会本能地想到 webassembly 技术。

没错,通过 webassembly 技术,我们可以在浏览器中执行 JS 之外的语言(比如 C++)。但是没必要自己去实现 SQL 执行逻辑了,站在巨人的肩膀上,直接使用开源的 sql.js 库,就可以在前端执行自己的 SQL 操作了。

核心代码在 src/core/sqlExecutor.ts 中,定义了初始化 DB 和执行 SQL 两个函数,很简单:

import initSqlJs, { Database, SqlJsStatic } from "sql.js";

/**
 * SQL 执行器
 *
 * @author coder_yupi https://github.com/liyupi
 */
let SQL: SqlJsStatic;

/**
 * 获取初始化 DB
 * @param initSql
 */
export const initDB = async (initSql?: string) => {
  if (!SQL) {
    SQL = await initSqlJs({
      // Required to load the wasm binary asynchronously
      locateFile: () =>
        "https://cdn.bootcdn.net/ajax/libs/sql.js/1.7.0/sql-wasm.wasm",
    });
  }
  // Create a database
  const db = new SQL.Database();
  if (initSql) {
    // Execute a single SQL string that contains multiple statements
    db.run(initSql); // Run the query without returning anything
  }
  return db;
};

/**
 * 执行 SQL
 * @param db
 * @param sql
 */
export const runSQL = (db: Database, sql: string) => {
  return db.exec(sql);
};

在关卡加载时,会先执行关卡对应的初始化 SQL 语句完成建表和导入示例数据,然后用户就可以编写 SQL 查询表中的数据了。

4、判题机制

和判题相关的代码全部集中定义在 src/core/result.ts 文件中,包括定义了几种执行状态,以及判断结果是否正确的函数。

如何判断用户的 SQL 语句是否正确呢?

不是直接去对比用户的输入语句和我们预设的答案是否一致(那样太死板了),而是依次执行以下 3 个操作:

  1. 分别提交用户的输入语句和答案语句,得到两份结果表
  2. 判断两个结果表输出的列名是否一致(名称和顺序都要一致)
  3. 判断两个结果表输出的数据是否一致

这里作者用了个 trick 方式来对比数据,直接把两份结果集转为 JSON 格式,对比 JSON 字符串是否一致即可,而不是多重 for 循环。

目录结构

  • public:公共静态资源
  • doc:文档相关资源
  • src
    • assets:静态资源
    • components:组件
      • CodeEditor.vue:代码编辑器
      • MdViewer.vue:Markdown 浏览
      • QuestionBoard.vue:题目面板(教程区)
      • SqlEditor.vue:SQL 编辑器(练习区)
      • SqlResult.vue:SQL 执行结果(结果区)
      • SqlResultTable.vue:SQL 结果表格
    • configs:配置
      • routes:路由
    • core:核心
      • sqlExecutor.ts:SQL 执行引擎
      • result.ts:执行结果相关变量和函数
      • globalStore.ts:全局状态管理
    • levels:关卡
      • custom:自定义关卡
      • main:主线关卡
        • level1:每个关卡都是一个单独的目录
          • createTable.sql:关卡依赖的建表语句
          • index.ts:关卡的定义
          • README.md:关卡教程
      • index.ts:定义了关卡相关变量和函数
      • level.d.ts:关卡类型定义
      • mainLevels:主线关卡列表
      • customLevels:自定义关卡列表
    • pages:页面
      • IndexPage.vue:主页
      • LevelsPage.vue:关卡页面
      • PlaygroundPage.vue:广场页面
    • App.vue:主页
    • main.ts:Vue 主文件
    • style.css:全局样式文件
    • vite-env.d.ts:环境定义
  • .eslintrc.js:代码规范
  • .gitignore:提交忽略文件
  • index.html:静态主页
  • package.json:项目管理
  • tsconfig.json:TS 配置
  • vite.config.ts:打包工具配置

贡献指南

欢迎各路好汉参与贡献,利人利己~

目前有几种推荐的贡献方式:

1、贡献关卡

在贡献关卡前,请确保你已经理解了本项目加载关卡的方式。

为保证教程的连贯性,更推荐贡献 自定义关卡 而不是主线关卡,更容易被合并。

贡献自定义关卡的步骤:

1)复制 src/levels/custom/自定义关卡模板 ,将目录名改为自己的关卡中文名

2)修改模板中的 createTable.sql 建表语句,导入默认数据

3)修改模板中的 index.ts 文件,设置关卡的中英文名、默认 SQL、答案 SQL、提示等

4)修改模板中的 README.md 文件,更改标题和题目内容,需要给出表结构信息、并且尽量把题目表达清楚(比如必须按照某个顺序输出)

5)在 customLevels.ts 文件中引入自定义的关卡。

注意,本项目仅支持 SQLite 语法(基本上是通用的 SQL)!不要使用太花里胡哨的函数!

自定义关卡

2、完善关卡

比如修复关卡的错误、优化关卡的文案使其更易于理解或增加更多干货、调整关卡的难度等。

3、项目扩展

本项目仅为鱼皮一人开发,时间和精力有限,很多地方没有做到完善,欢迎大家给项目进行扩展,打造属于自己的 SQL 之子、SQL 之孙、SQL 之曾孙系列产品。。。

一些可能的扩展思路:

  1. 点击 “提交” 题目后,自动展开执行结果区域
  2. 过关后,给出更友好的过关提示,可以更方便地到达下一关
  3. 支持 SQL 一键格式化
  4. 优化关卡加载机制,按需加载
  5. 给项目增加一个后端,用数据库来存放关卡数据,并且支持在线提交 / 审核关卡
  6. 增加过关排行榜

感谢阅读,也欢迎加入 作者的编程学习圈,学习更多原创项目~

sql-mother's People

Contributors

coder66y avatar cq2021-coder avatar equationzhao avatar htfc786 avatar liyupi avatar ming-z-y avatar trumps4dayz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sql-mother's Issues

只是过来抖个机灵。

关于level16的独特解法。
-- 请在此处输入 SQL
-- select sum(score) total_score,avg(score) avg_score,max(score) max_score,min(score) min_score from student

select 3073 as total_score,307.3 as avg_score ,600 as max_score ,0 as min_score

hhhh~~

查询进阶 - 关联查询 - cross join

示例的查询结果和SQL代码好像不太匹配?
如果按照示例的SQL代码,跑一遍出来的结果应该是
+----------+--------+------------+---------+
| emp_name | salary | department | manager |
+----------+--------+------------+---------+
| 小明 | 5000 | 技术部 | 张三 |
| 小明 | 5000 | 技术部 | 李四 |
| 小明 | 5000 | 技术部 | 王五 |
| 李华 | 4500 | 销售部 | 张三 |
| 李华 | 4500 | 销售部 | 李四 |
| 李华 | 4500 | 销售部 | 王五 |
| 鸡哥 | 6000 | 财务部 | 张三 |
| 鸡哥 | 6000 | 财务部 | 李四 |
| 鸡哥 | 6000 | 财务部 | 王五 |
+----------+--------+------------+---------+
但这个和示例给出的结果不太相同,如果把SQL语句改成
mysql> select e.emp_name, e.salary, d.department, d.manager
-> from employees e, departments d
-> order by emp_name asc;
这样出来的结果是
+----------+--------+------------+---------+
| emp_name | salary | department | manager |
+----------+--------+------------+---------+
| 小明 | 5000 | 技术部 | 张三 |
| 小明 | 5000 | 财务部 | 李四 |
| 小明 | 5000 | 销售部 | 王五 |
| 李华 | 4500 | 技术部 | 张三 |
| 李华 | 4500 | 财务部 | 李四 |
| 李华 | 4500 | 销售部 | 王五 |
| 鸡哥 | 6000 | 技术部 | 张三 |
| 鸡哥 | 6000 | 财务部 | 李四 |
| 鸡哥 | 6000 | 销售部 | 王五 |
+----------+--------+------------+---------+
这样就和示例的结果相同了
所以问题应该是department的归属问题?

sql 执行失败

结果

image

环境

Macbook air m2
macOS Sonoma 14.0 23A5312d arm64
node v20.5.1
npm 9.8.0
> npm list
[email protected] /Users/equationzhao/sql-mother
├── @bytemd/[email protected]
├── @bytemd/[email protected]
├── @bytemd/[email protected]
├── @types/[email protected]
├── @typescript-eslint/[email protected]
├── @typescript-eslint/[email protected]
├── @vitejs/[email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]

冒险者和金币答案

SELECT adventurer_id, adventurer_name, SUM(reward_coins) AS total_reward_coins FROM rewards GROUP BY adventurer_id, adventurer_name ORDER BY total_reward_coins DESC LIMIT 3;

group by adventurer_id 也对🐔

第 23、24 关的子查询教学中的示例代码是否有问题?

看示例代码的时候,发现示例给的代码是跑不出来的。是否存在错误?
以下是我自己测试可以运行的代码
-- Level 23

-- My Answer of Example:
-- 主查询
SELECT c.name, o.total_amount
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
WHERE c.customer_id IN (
	-- 子查询
	SELECT customer_id 
	FROM orders
	WHERE total_amount > 200
)

====================
-- Level 24

-- 主查询
SELECT c.name, o.total_amount
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
WHERE EXISTS (
	-- 子查询
	SELECT DISTINCT customer_id
	FROM orders
)

第 20 个关卡 cross join 的描述准确性

题目要求将学生表和班级表的所有行组合在一起,并返回学生姓名(student_name)、学生年龄(student_age)、班级编号(class_id)以及班级名称(class_name)。

提供的答案是 select s.name student_name, s.age student_age, s.class_id class_id, c.name class_name from student s, class c;

因为 class 表里同样有 id 字段,表示班级编号,将 s.class_id class_id 改为 c.id class_id 之后满足题目的要求,但查询结果与答案不一致,无法通过关卡。

网站数据库是什么?应该不是mysql吧?

请问该练习网站的数据库是什么?

select datetime() as 'datetime'

http://sqlmother.yupi.icu/#/learn 上面运行没有问题,

在我本地 mysql:5.7.36 上面执行报错,提示

select datetime() as 'datetime'
> 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '() as 'datetime'' at line 6
> Time: 0.001s

而换成

select CURRENT_TIMESTAMP as 'datetime';

则可以得的到期待的

datetime
2023-09-26 16:15:15

自定义关卡-冒险者和金币

看答案中group_by adventurer_id,adventurer_name。但是adventurer_id已经是用户的唯一标识了,所以group_by adventurer_id应该就够了,没必要再加上adventurer_name了吧?

第 29 关的教学

介绍“分配唯一连续排名”,介绍中却没有给出并列的例子。

能问问是什么问题呢 本地跑不动

failed to load config from C:\Users\LiWeiJun\Desktop\sql-mother-master\vite.config.ts
error when starting dev server:
Error:
You installed esbuild for another platform than the one you're currently using.
This won't work because esbuild is written with native code and needs to
install a platform-specific binary executable.

Specifically the "esbuild-windows-64" package is present but this platform
needs the "esbuild-windows-32" package instead. People often get into this
situation by installing esbuild on Windows or macOS and copying "node_modules"
into a Docker image that runs Linux, or by copying "node_modules" between
Windows and WSL environments.

If you are installing with npm, you can try not copying the "node_modules"
directory when you copy the files over, and running "npm ci" or "npm install"
on the destination platform after the copy. Or you could consider using yarn
instead of npm which has built-in support for installing a package on multiple
platforms simultaneously.

If you are installing with yarn, you can try listing both this platform and the
other platform in your ".yarnrc.yml" file using the "supportedArchitectures"
feature: https://yarnpkg.com/configuration/yarnrc/#supportedArchitectures
Keep in mind that this means multiple copies of esbuild will be present.

Another alternative is to use the "esbuild-wasm" package instead, which works
the same way on all platforms. But it comes with a heavy performance cost and
can sometimes be 10x slower than the "esbuild" package, so you may also not
want to do that.

at generateBinPath (C:\Users\LiWeiJun\Desktop\sql-mother-master\node_modules\esbuild\lib\main.js:1808:17)
at esbuildCommandAndArgs (C:\Users\LiWeiJun\Desktop\sql-mother-master\node_modules\esbuild\lib\main.js:1886:33)
at ensureServiceIsRunning (C:\Users\LiWeiJun\Desktop\sql-mother-master\node_modules\esbuild\lib\main.js:2051:25)
at build (C:\Users\LiWeiJun\Desktop\sql-mother-master\node_modules\esbuild\lib\main.js:1942:26)
at bundleConfigFile (file:///C:/Users/LiWeiJun/Desktop/sql-mother-master/node_modules/vite/dist/node/chunks/dep-2faf2534.js:63043:26)
at loadConfigFromFile (file:///C:/Users/LiWeiJun/Desktop/sql-mother-master/node_modules/vite/dist/node/chunks/dep-2faf2534.js:63019:31)
at resolveConfig (file:///C:/Users/LiWeiJun/Desktop/sql-mother-master/node_modules/vite/dist/node/chunks/dep-2faf2534.js:62643:34)
at createServer (file:///C:/Users/LiWeiJun/Desktop/sql-mother-master/node_modules/vite/dist/node/chunks/dep-2faf2534.js:61943:26)
at CAC.<anonymous> (file:///C:/Users/LiWeiJun/Desktop/sql-mother-master/node_modules/vite/dist/node/cli.js:707:30)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.