双列商品分类页面——uview+uniapp实现(单向联动+双向联动)_uview 购物菜单-CSDN博客

mikel阅读(61)

来源: 双列商品分类页面——uview+uniapp实现(单向联动+双向联动)_uview 购物菜单-CSDN博客

一个电商的app,商品的展示是必不可少的,联动分类展示是很常见的:

单向联动功能

实现效果:

  1. 左侧是分类列表,有很多分类,可以滑动并点击
  2. 右侧是该分类下的商品列表
  3. 仅支持左侧点击后右侧内容改变,为单向联动展示
    在这里插入图片描述

    <view class=”u-menu-wrap”>
    <scroll-view scroll-y scroll-with-animation class=”u-tab-view menu-scroll-view” :scroll-top=”scrollTop”>
    <view v-for=”(item,index) in tabbar” :key=”index” class=”u-tab-item” :class=”[current==index ? ‘u-tab-item-active’ : ”]”
    :data-current=”index” @tap.stop=”swichMenu(index)”>
    <text class=”u-line-1″>{{item.name}}</text>
    </view>
    </scroll-view>
    <block v-for=”(item,index) in tabbar” :key=”index”>
    <scroll-view scroll-y class=”right-box” v-if=”current==index”>
    <view class=”page-view”>
    <view class=”class-item”>
    <view class=”item-title”>
    <text>{{item.name}}</text>
    </view>
    <view class=”item-container”>
    <view class=”thumb-box” v-for=”(item1, index1) in item.foods” :key=”index1″>
    <image class=”item-menu-image” :src=”item1.icon” mode=””></image>
    <view class=”item-menu-name”>{{item1.name}}</view>
    </view>
    </view>
    </view>
    </view>
    </scroll-view>
    </block>
    </view>
    <script>
    import classifyData from “@/common/classify.data.js”;//是本地模拟的数据
    export default {
    data() {
    return {
    tabbar: classifyData,
    scrollTop: 0, //tab标题的滚动条位置
    current: 0, // 预设当前项的值
    menuHeight: 0, // 左边菜单的高度
    menuItemHeight: 0, // 左边菜单item的高度
    }
    },
    methods: {
    // 点击左边的栏目切换
    async swichMenu(index) {
    if(index == this.current) return ;
    this.current = index;
    // 如果为0,意味着尚未初始化
    if(this.menuHeight == 0 || this.menuItemHeight == 0) {
    await this.getElRect(‘menu-scroll-view’, ‘menuHeight’);
    await this.getElRect(‘u-tab-item’, ‘menuItemHeight’);
    }
    // 将菜单菜单活动item垂直居中
    this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 – this.menuHeight / 2;
    },
    // 获取一个目标元素的高度
    getElRect(elClass, dataVal) {
    new Promise((resolve, reject) => {
    const query = uni.createSelectorQuery().in(this);
    query.select(‘.’ + elClass).fields({size: true},res => {
    // 如果节点尚未生成,res值为null,循环调用执行
    if(!res) {
    setTimeout(() => {
    this.getElRect(elClass);
    }, 10);
    return ;
    }
    this[dataVal] = res.height;
    }).exec();
    })
    }
    }
    }
    </script>
    <style lang=”scss” scoped>
    .u-wrap {
    height: calc(100vh);
    /* #ifdef H5 */
    height: calc(100vh – var(–window-top));
    /* #endif */
    display: flex;
    flex-direction: column;
    }
    .u-search-box {
    padding: 18rpx 30rpx;
    }
    .u-menu-wrap {
    flex: 1;
    display: flex;
    overflow: hidden;
    }
    .u-search-inner {
    background-color: rgb(234, 234, 234);
    border-radius: 100rpx;
    display: flex;
    align-items: center;
    padding: 10rpx 16rpx;
    }
    .u-search-text {
    font-size: 26rpx;
    color: $u-tips-color;
    margin-left: 10rpx;
    }
    .u-tab-view {
    width: 200rpx;
    height: 100%;
    }
    .u-tab-item {
    height: 110rpx;
    background: #f6f6f6;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 26rpx;
    color: #444;
    font-weight: 400;
    line-height: 1;
    }
    .u-tab-item-active {
    position: relative;
    color: #000;
    font-size: 30rpx;
    font-weight: 600;
    background: #fff;
    }
    .u-tab-item-active::before {
    content: “”;
    position: absolute;
    border-left: 4px solid $u-type-primary;
    height: 32rpx;
    left: 0;
    top: 39rpx;
    }
    .u-tab-view {
    height: 100%;
    }
    .right-box {
    background-color: rgb(250, 250, 250);
    }
    .page-view {
    padding: 16rpx;
    }
    .class-item {
    margin-bottom: 30rpx;
    background-color: #fff;
    padding: 16rpx;
    border-radius: 8rpx;
    }
    .item-title {
    font-size: 26rpx;
    color: $u-main-color;
    font-weight: bold;
    }
    .item-menu-name {
    font-weight: normal;
    font-size: 24rpx;
    color: $u-main-color;
    }
    .item-container {
    display: flex;
    flex-wrap: wrap;
    }
    .thumb-box {
    width: 33.333333%;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    margin-top: 20rpx;
    }
    .item-menu-image {
    width: 120rpx;
    height: 120rpx;
    }
    </style>

    双向联动

    实现效果:

    1. 左侧是分类列表,有很多分类,可以滑动并点击
    2. 右侧是该分类下的商品列表
    3. 左侧点击时右侧展示对应分类的商品,右侧支持滑动,滑动时左侧分类也会相应改变

    在这里插入图片描述

    双向联动代码

    附录classify.data.js中的数据:

  4. export default[
    {
    “name”: “女装”,
    “foods”: [
    {
    “name”: “A字裙”,
    “key”: “A字裙”,
    “icon”: “https://cdn.uviewui.com/uview/common/classify/1/1.jpg”,
    “cat”: 10
    },
    {
    “name”: “T恤”,
    “key”: “T恤”,
    “icon”: “https://cdn.uviewui.com/uview/common/classify/1/2.jpg”,
    “cat”: 10
    },
    {
    “name”: “半身裙”,
    “key”: “半身裙”,
    “icon”: “https://cdn.uviewui.com/uview/common/classify/1/3.jpg”,
    “cat”: 10
    },
    ]
    }
    ]
  5. <view class=”u-menu-wrap”>
    <scroll-view scroll-y scroll-with-animation class=”u-tab-view menu-scroll-view” :scroll-top=”scrollTop”
    :scroll-into-view=”itemId”>
    <view v-for=”(item,index) in tabbar” :key=”index” class=”u-tab-item” :class=”[current == index ? ‘u-tab-item-active’ : ”]”
    @tap.stop=”swichMenu(index)”>
    <text class=”u-line-1″>{{item.name}}</text>
    </view>
    </scroll-view>
    <scroll-view :scroll-top=”scrollRightTop” scroll-y scroll-with-animation class=”right-box” @scroll=”rightScroll”>
    <view class=”page-view”>
    <view class=”class-item” :id=”‘item’ + index” v-for=”(item , index) in tabbar” :key=”index”>
    <view class=”item-title”>
    <text>{{item.name}}</text>
    </view>
    <view class=”item-container”>
    <view class=”thumb-box” v-for=”(item1, index1) in item.foods” :key=”index1″>
    <image class=”item-menu-image” :src=”item1.icon” mode=””></image>
    <view class=”item-menu-name”>{{item1.name}}</view>
    </view>
    </view>
    </view>
    </view>
    </scroll-view>
    </view>
    <script>
    import classifyData from ‘@/common/classify.data.js’;
    export default {
    data() {
    return {
    scrollTop: 0, //tab标题的滚动条位置
    oldScrollTop: 0,
    current: 0, // 预设当前项的值
    menuHeight: 0, // 左边菜单的高度
    menuItemHeight: 0, // 左边菜单item的高度
    itemId: ”, // 栏目右边scroll-view用于滚动的id
    tabbar: classifyData,
    menuItemPos: [],
    arr: [],
    scrollRightTop: 0, // 右边栏目scroll-view的滚动条高度
    timer: null, // 定时器
    }
    },
    onReady() {
    this.getMenuItemTop()
    },
    methods: {
    // 点击左边的栏目切换
    async swichMenu(index) {
    if(this.arr.length == 0) {
    await this.getMenuItemTop();
    }
    if (index == this.current) return;
    this.scrollRightTop = this.oldScrollTop;
    this.$nextTick(function(){
    this.scrollRightTop = this.arr[index];
    this.current = index;
    this.leftMenuStatus(index);
    })
    },
    // 获取一个目标元素的高度
    getElRect(elClass, dataVal) {
    new Promise((resolve, reject) => {
    const query = uni.createSelectorQuery().in(this);
    query.select(‘.’ + elClass).fields({
    size: true
    }, res => {
    // 如果节点尚未生成,res值为null,循环调用执行
    if (!res) {
    setTimeout(() => {
    this.getElRect(elClass);
    }, 10);
    return;
    }
    this[dataVal] = res.height;
    resolve();
    }).exec();
    })
    },
    // 观测元素相交状态
    async observer() {
    this.tabbar.map((val, index) => {
    let observer = uni.createIntersectionObserver(this);
    // 检测右边scroll-view的id为itemxx的元素与right-box的相交状态
    // 如果跟.right-box底部相交,就动态设置左边栏目的活动状态
    observer.relativeTo(‘.right-box’, {
    top: 0
    }).observe(‘#item’ + index, res => {
    if (res.intersectionRatio > 0) {
    let id = res.id.substring(4);
    this.leftMenuStatus(id);
    }
    })
    })
    },
    // 设置左边菜单的滚动状态
    async leftMenuStatus(index) {
    this.current = index;
    // 如果为0,意味着尚未初始化
    if (this.menuHeight == 0 || this.menuItemHeight == 0) {
    await this.getElRect(‘menu-scroll-view’, ‘menuHeight’);
    await this.getElRect(‘u-tab-item’, ‘menuItemHeight’);
    }
    // 将菜单活动item垂直居中
    this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 – this.menuHeight / 2;
    },
    // 获取右边菜单每个item到顶部的距离
    getMenuItemTop() {
    new Promise(resolve => {
    let selectorQuery = uni.createSelectorQuery();
    selectorQuery.selectAll(‘.class-item’).boundingClientRect((rects) => {
    // 如果节点尚未生成,rects值为[](因为用selectAll,所以返回的是数组),循环调用执行
    if(!rects.length) {
    setTimeout(() => {
    this.getMenuItemTop();
    }, 10);
    return ;
    }
    rects.forEach((rect) => {
    // 这里减去rects[0].top,是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
    this.arr.push(rect.top – rects[0].top);
    resolve();
    })
    }).exec()
    })
    },
    // 右边菜单滚动
    async rightScroll(e) {
    this.oldScrollTop = e.detail.scrollTop;
    if(this.arr.length == 0) {
    await this.getMenuItemTop();
    }
    if(this.timer) return ;
    if(!this.menuHeight) {
    await this.getElRect(‘menu-scroll-view’, ‘menuHeight’);
    }
    setTimeout(() => { // 节流
    this.timer = null;
    // scrollHeight为右边菜单垂直中点位置
    let scrollHeight = e.detail.scrollTop + this.menuHeight / 2;
    for (let i = 0; i < this.arr.length; i++) {
    let height1 = this.arr[i];
    let height2 = this.arr[i + 1];
    // 如果不存在height2,意味着数据循环已经到了最后一个,设置左边菜单为最后一项即可
    if (!height2 || scrollHeight >= height1 && scrollHeight < height2) {
    this.leftMenuStatus(i);
    return ;
    }
    }
    }, 10)
    }
    }
    }
    </script>
    <style lang=”scss” scoped>
    .u-wrap {
    height: calc(100vh);
    /* #ifdef H5 */
    height: calc(100vh – var(–window-top));
    /* #endif */
    display: flex;
    flex-direction: column;
    }
    .u-search-box {
    padding: 18rpx 30rpx;
    }
    .u-menu-wrap {
    flex: 1;
    display: flex;
    overflow: hidden;
    }
    .u-search-inner {
    background-color: rgb(234, 234, 234);
    border-radius: 100rpx;
    display: flex;
    align-items: center;
    padding: 10rpx 16rpx;
    }
    .u-search-text {
    font-size: 26rpx;
    color: $u-tips-color;
    margin-left: 10rpx;
    }
    .u-tab-view {
    width: 200rpx;
    height: 100%;
    }
    .u-tab-item {
    height: 110rpx;
    background: #f6f6f6;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 26rpx;
    color: #444;
    font-weight: 400;
    line-height: 1;
    }
    .u-tab-item-active {
    position: relative;
    color: #000;
    font-size: 30rpx;
    font-weight: 600;
    background: #fff;
    }
    .u-tab-item-active::before {
    content: “”;
    position: absolute;
    border-left: 4px solid $u-type-primary;
    height: 32rpx;
    left: 0;
    top: 39rpx;
    }
    .u-tab-view {
    height: 100%;
    }
    .right-box {
    background-color: rgb(250, 250, 250);
    }
    .page-view {
    padding: 16rpx;
    }
    .class-item {
    margin-bottom: 30rpx;
    background-color: #fff;
    padding: 16rpx;
    border-radius: 8rpx;
    }
    .class-item:last-child {
    min-height: 100vh;
    }
    .item-title {
    font-size: 26rpx;
    color: $u-main-color;
    font-weight: bold;
    }
    .item-menu-name {
    font-weight: normal;
    font-size: 24rpx;
    color: $u-main-color;
    }
    .item-container {
    display: flex;
    flex-wrap: wrap;
    }
    .thumb-box {
    width: 33.333333%;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    margin-top: 20rpx;
    }
    .item-menu-image {
    width: 120rpx;
    height: 120rpx;
    }
    </style>

他又又来了,c#开源sql解析引擎类库【SqlParser.Net 1.0】正式发布,它可以帮助你简单快速高效的解析和处理sql - 三合视角 - 博客园

mikel阅读(63)

来源: 他又又来了,c#开源sql解析引擎类库【SqlParser.Net 1.0】正式发布,它可以帮助你简单快速高效的解析和处理sql – 三合视角 – 博客园

背景

hi 大家好,我是三合,在过往的岁月中,我曾经想过要写以下这些工具

  1. 写一个通过拦截业务系统所有SQL,然后根据这些SQL自动分析表与表,字段与字段之间是如何关联的工具,即SQL血缘分析工具
  2. 想着动态改写sql,比如给where动态添加一个条件。
  3. 写一个sql格式化工具
  4. 写一个像mycat那样的分库分表中间件
  5. 写一个sql防火墙,防止出现where 1=1后没有其他条件导致查询全表
  6. 写一个数据库之间的sql翻译工具,比如把SQLServer的sql自动翻译为oracle的sql

但是无一例外,都失败了,因为要实现以上这些需求,都需要一个核心的类库,即sql解析引擎,遗憾的是,我没有找到合适的,这是我当初寻找的轨迹

  1. 我发现了tsql-parser,但他只支持sql server,所以只能pass。
  2. 然后我又发现了SqlParser-cs,
    他的语法树解析出来像这样,
JsonConvert.SerializeObject(statements.First(), Formatting.Indented)
// Elided for readability
{
   "Query": {
      "Body": {
         "Select": {
            "Projection": [
               {
                  "Expression": {
                     "Ident": {
                        "Value": "a",
                        "QuoteStyle": null
                     }
                  }
               }
	...

额,怎么说呢,这语法树也太丑了点吧,同时非常难以理解,跟我想象中的完全不一样啊,于是也只能pass。

  1. 接下来我又发现了另外一些基于antlr来解析sql的类库,比如SQLParser,因为代码是antlr自动生成的,比较难以进行手动优化,所以还是pass。
  2. 最后我还发现了另外一个gsp的sqlparser,但它是收费的,而且巨贵无比,也pass。

找了一圈下来,我发现符合我要求的类库并不存在,所以我上面的那些想法,也一度搁浅了,但每一次的搁浅,都会使我内心的不甘加重一分,终于有一天,我下定决心,自己动手,丰衣足食,所以最近花了大概3个月时间,从头开始写了一个sql解析引擎,包括词法解析器到语法分析器,不依赖任何第三方组件,纯C#代码,在通过了156个各种各样场景的单元测试以及各种真实的业务环境验证后,今天它SqlParser.Net1.0.0正式发布了,本项目基于MIT协议开源,有以下优点,

  1. 支持5大数据库,oracle,SQLServer,mysql,pgsql以及sqlite。
  2. 极致的速度,解析普通sql,时间基本在0.3毫秒以下,当然了,sql越长,解析需要的时间就越长。
  3. 文档完善,众所周知,我三合的开源项目,一向是文档齐全且简单易懂,做到看完就能上手,同时,我也会根据用户的反馈不断的补充以及完善文档。
  4. 代码简洁易懂

SqlParser.Net存在的意义

SqlParser.Net是一个免费,功能全面且高性能的sql解析引擎类库,它可以帮助你简单快速高效的解析和处理sql。

Getting Started

接下来,我将介绍SqlParser.Net的用法

通过Nuget安装

你可以运行以下命令在你的项目中安装 SqlParser.Net 。

PM> Install-Package SqlParser.Net

支持框架

netstandard2.0

从最简单的demo开始

让我们一起看一个最简单的select语句是如何解析的

var sql = "select * from test";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            }
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test"
            }
        }
    }
};

以上面为例子,抽象语法树的所有叶子节点均为sqlExpression的子类,且各种sqlExpression节点可以互相嵌套,组成一颗复杂无比的树,其他sql解析引擎还分为statement和expression,我认为过于复杂,所以全部统一为sqlExpression,顶级的sqlExpression总共分为4种,

  1. 查询语句(SqlSelectExpression)
  2. 插入语句(SqlInsertExpression)
  3. 删除语句(SqlDeleteExpression)
  4. 更新语句(SqlUpdateExpression)

这4种顶级语句中,我认为最复杂的是查询语句,因为查询组合非常多,要兼容各种各样的情况,其他3种反而非常简单。现阶段,sqlExpression的子类一共有38种,我将在下面的演示中,结合例子给大家讲解一下。

1. Select查询语句

如上例子,SqlSelectExpression代表一个查询语句,SqlSelectQueryExpression则是真正具体的查询语句,他包括了

  1. 要查询的所有列(Columns字段)
  2. 数据源(From字段)
  3. 条件过滤语句(Where字段)
  4. 分组语句(GroupBy字段)
  5. 排序语句(OrderBy字段)
  6. 分页语句(Limit字段)
  7. Into语句(sql server专用,如SELECT id,name into test14 from TEST t)
  8. ConnectBy语句(oracle专用,如SELECT LEVEL l FROM DUAL CONNECT BY NOCYCLE LEVEL<=100)
  9. WithSubQuerys语句,公用表表达式,即CTE

其中Columns是一个列表,他的每一个子项都是一个SqlSelectItemExpression,他的body代表一个逻辑子句,逻辑子句的值,可以为以下这些

  1. 字段,如name,
  2. 二元表达式,如t.age+3
  3. 函数调用,如LOWER(t.NAME)
  4. 一个完整的查询语句,如SELECT name FROM TEST2 t2

包括order by,partition by,group by,between,in,case when后面跟着的都是逻辑子句,这个稍后会演示,在这个例子中,因为是要查询所有列,所以仅有一个SqlSelectItemExpression,他的body是SqlAllColumnExpression(代表所有列),From代表要查询的数据源,在这里仅单表查询,所以From的值为SqlTableExpression(代表单表),表名是一个SqlIdentifierExpression,即标识符表达式,表示这是一个标识符,在SQL中,标识符(Identifier)是用于命名数据库对象的名称。这些对象可以包括表、列、索引、视图、模式、数据库等。标识符使得我们能够引用和操作这些对象,在这里,标识符的值为test,表示表名为test。

1.1 查询返回列的各种情形

1.1.1 查询指定字段

var sql = "select id AS bid,t.NAME testName  from test t";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlIdentifierExpression()
                {
                    Value = "id",
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "bid",
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "NAME",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "t",
                    },
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "testName",
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
    },
};


在上面这个例子中,我们指定了要查询2个字段,分别是id和t.NAME,此时Columns列表里有2个值,
第一个SqlSelectItemExpression包含了

  1. 主体,即body字段,在本例子中他的值是一个SqlIdentifierExpression表达式,值为id,表示列名为id,
  2. 别名,即Alias字段,在本例子中他也是一个SqlIdentifierExpression,值为bid,代表列别名为bid,

第二个SqlSelectItemExpression的body里是一个SqlPropertyExpression,代表这是一个属性表达式,SqlPropertyExpression它包含了

  1. 表名,即Table字段,值为t,即表名为t
  2. 属性名,即Name字段,值为Name,即属性名为name

合起来则代表t表的name字段,而第二个SqlSelectItemExpression也有列别名,即testName,这个查询也是单表查询,但SqlTableExpression他多了一个Alias别名字段,即表示,表别名为t。

1.1.2 查询列为二元表达式的情况

var sql = "select 1+2 from test";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlBinaryExpression()
                {
                    Left = new SqlNumberExpression()
                    {
                        Value = 1M,
                    },
                    Operator = SqlBinaryOperator.Add,
                    Right = new SqlNumberExpression()
                    {
                        Value = 2M,
                    },
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
        },
    },
};

在这个例子中,要查询的字段的值为一个二元表达式SqlBinaryExpression,他包含了

  1. 左边部分,即Left字段,值为一个SqlNumberExpression,即数字表达式,它的值为1
  2. 右边部分,即Right字段,值为一个SqlNumberExpression,即数字表达式,它的值为2
  3. 中间符号,即Operator字段,值为add,即加法

这个例子证明了,SqlSelectItemExpression代表一个逻辑子句,而不仅仅是某个字段。

1.1.3 查询列为字符串/数字/布尔值的情况

var sql = "select ''' ''',3,true FROM test";
var sqlAst = DbUtils.Parse(sql, DbType.MySql);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlStringExpression()
                {
                    Value = "' '"
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlNumberExpression()
                {
                    Value = 3M,
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlBoolExpression()
                {
                    Value = true
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
        },
    },
};


在这个例子中,要查询的3个字段为字符串,数字和布尔值,字符串表达式即SqlStringExpression,body里即字符串的值’ ‘,数字表达式即SqlNumberExpression,值为3,布尔表达式即SqlBoolExpression,值为true;

1.1.4 查询列为函数调用的情况

1.1.4.1 简单的函数调用
var sql = "select LOWER(name)  FROM test";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlFunctionCallExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "LOWER",
                    },
                    Arguments = new List<SqlExpression>()
                    {
                        new SqlIdentifierExpression()
                        {
                            Value = "name",
                        },
                    },
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
        },
    },
};


在这个例子中,要查询的表达式是一个函数调用,函数调用表达式即SqlFunctionCallExpression,它包含了

  1. 函数名,即Name字段,值为LOWER,
  2. 函数参数列表,即Arguments字段,列表里只有一个值,即函数只有一个参数,且参数的值为name
1.1.4.2 带有over子句的函数调用
var sql = "SELECT t.*, ROW_NUMBER() OVER ( PARTITION BY t.ID  ORDER BY t.NAME,t.ID) as rnum FROM TEST t";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "*",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "t",
                    },
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlFunctionCallExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "ROW_NUMBER",
                    },
                    Over = new SqlOverExpression()
                    {
                        PartitionBy = new SqlPartitionByExpression()
                        {
                            Items = new List<SqlExpression>()
                            {
                                new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "ID",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "t",
                                    },
                                },
                            },
                        },
                        OrderBy = new SqlOrderByExpression()
                        {
                            Items = new List<SqlOrderByItemExpression>()
                            {
                                new SqlOrderByItemExpression()
                                {
                                    Body = new SqlPropertyExpression()
                                    {
                                        Name = new SqlIdentifierExpression()
                                        {
                                            Value = "NAME",
                                        },
                                        Table = new SqlIdentifierExpression()
                                        {
                                            Value = "t",
                                        },
                                    },
                                },
                                new SqlOrderByItemExpression()
                                {
                                    Body = new SqlPropertyExpression()
                                    {
                                        Name = new SqlIdentifierExpression()
                                        {
                                            Value = "ID",
                                        },
                                        Table = new SqlIdentifierExpression()
                                        {
                                            Value = "t",
                                        },
                                    },
                                },
                            },
                        },
                    },
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "rnum",
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
    },
};



在这个例子中,SqlFunctionCallExpression它除了常规字段外,还包含了Over子句,具体有以下这些

  1. 函数名,即Name字段,值为ROW_NUMBER,
  2. 函数参数列表,即Arguments字段,值为null,即无参数
  3. Over子句,即Over字段,他的值为一个SqlOverExpression表达式,SqlOverExpression本身又包含了以下内容
    1. PartitionBy分区子句,值为一个SqlPartitionByExpression表达式,表达式的内容也非常简单,只有一个Items,即一个分区表达式的列表,在这个例子中,列表里只有一个值SqlPropertyExpression,即根据t.id分区
    2. OrderBy排序子句,值为SqlOrderByExpression表达式,表达式的内容也非常简单,只有一个Items,即一个排序表达式的列表,列表里的值为SqlOrderByItemExpression,即排序子项表达式,排序子项表达式里又包含了以下内容
      1. 排序依据,即Body字段,在这个例子中,排序依据是2个SqlPropertyExpression表达式,即根据t.NAME,t.ID排序
      2. 排序类型,即OrderByType字段,值为Asc或者Desc,默认为asc,在这2个例子中,默认排序类型都是asc
1.1.4.3 带有within group子句的函数调用
var sql = "select name,PERCENTILE_CONT(0.5) within group(order by \"number\") from TEST5 group by name";
var sqlAst = DbUtils.Parse(sql, DbType.Pgsql);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlIdentifierExpression()
                {
                    Value = "name",
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlFunctionCallExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "PERCENTILE_CONT",
                    },
                    WithinGroup = new SqlWithinGroupExpression()
                    {
                        OrderBy = new SqlOrderByExpression()
                        {
                            Items = new List<SqlOrderByItemExpression>()
                            {
                                new SqlOrderByItemExpression()
                                {
                                    Body = new SqlIdentifierExpression()
                                    {
                                        Value = "number",
                                        LeftQualifiers = "\"",
                                        RightQualifiers = "\"",
                                    },
                                },
                            },
                        },
                    },
                    Arguments = new List<SqlExpression>()
                    {
                        new SqlNumberExpression()
                        {
                            Value = 0.5M,
                        },
                    },
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST5",
            },
        },
        GroupBy = new SqlGroupByExpression()
        {
            Items = new List<SqlExpression>()
            {
            new SqlIdentifierExpression()
            {
                Value = "name",
            },
            },
        },
    },
};

在这个例子中,SqlFunctionCallExpression它除了常规字段外,还包含了within group子句,具体有以下这些

  1. 函数名,即Name字段,值为PERCENTILE_CONT,
  2. 函数参数列表,即Arguments字段,列表里只有一项,表示只有1个参数,参数是SqlNumberExpression表达式,值为0.5
  3. within group子句,即WithinGroup字段,他的值为一个SqlWithinGroupExpression表达式,SqlWithinGroupExpression又包含了OrderBy排序子句,这里根据number字段排序

1.1.5 查询列为子查询的情况

var sql = "select c.*, (select a.name as province_name from portal_area a where a.id = c.province_id) as province_name, (select a.name as city_name from portal_area a where a.id = c.city_id) as city_name, (CASE WHEN c.area_id IS NULL THEN NULL ELSE (select a.name as area_name from portal_area a where a.id = c.area_id)  END )as area_name  from portal.portal_company c";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "*",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "c",
                    },
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlSelectExpression()
                {
                    Query = new SqlSelectQueryExpression()
                    {
                        Columns = new List<SqlSelectItemExpression>()
                        {
                            new SqlSelectItemExpression()
                            {
                                Body = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "name",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "a",
                                    },
                                },
                                Alias = new SqlIdentifierExpression()
                                {
                                    Value = "province_name",
                                },
                            },
                        },
                        From = new SqlTableExpression()
                        {
                            Name = new SqlIdentifierExpression()
                            {
                                Value = "portal_area",
                            },
                            Alias = new SqlIdentifierExpression()
                            {
                                Value = "a",
                            },
                        },
                        Where = new SqlBinaryExpression()
                        {
                            Left = new SqlPropertyExpression()
                            {
                                Name = new SqlIdentifierExpression()
                                {
                                    Value = "id",
                                },
                                Table = new SqlIdentifierExpression()
                                {
                                    Value = "a",
                                },
                            },
                            Operator = SqlBinaryOperator.EqualTo,
                            Right = new SqlPropertyExpression()
                            {
                                Name = new SqlIdentifierExpression()
                                {
                                    Value = "province_id",
                                },
                                Table = new SqlIdentifierExpression()
                                {
                                    Value = "c",
                                },
                            },
                        },
                    },
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "province_name",
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlSelectExpression()
                {
                    Query = new SqlSelectQueryExpression()
                    {
                        Columns = new List<SqlSelectItemExpression>()
                        {
                            new SqlSelectItemExpression()
                            {
                                Body = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "name",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "a",
                                    },
                                },
                                Alias = new SqlIdentifierExpression()
                                {
                                    Value = "city_name",
                                },
                            },
                        },
                        From = new SqlTableExpression()
                        {
                            Name = new SqlIdentifierExpression()
                            {
                                Value = "portal_area",
                            },
                            Alias = new SqlIdentifierExpression()
                            {
                                Value = "a",
                            },
                        },
                        Where = new SqlBinaryExpression()
                        {
                            Left = new SqlPropertyExpression()
                            {
                                Name = new SqlIdentifierExpression()
                                {
                                    Value = "id",
                                },
                                Table = new SqlIdentifierExpression()
                                {
                                    Value = "a",
                                },
                            },
                            Operator = SqlBinaryOperator.EqualTo,
                            Right = new SqlPropertyExpression()
                            {
                                Name = new SqlIdentifierExpression()
                                {
                                    Value = "city_id",
                                },
                                Table = new SqlIdentifierExpression()
                                {
                                    Value = "c",
                                },
                            },
                        },
                    },
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "city_name",
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlCaseExpression()
                {
                    Items = new List<SqlCaseItemExpression>()
                    {
                        new SqlCaseItemExpression()
                        {
                            Condition = new SqlBinaryExpression()
                            {
                                Left = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "area_id",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "c",
                                    },
                                },
                                Operator = SqlBinaryOperator.Is,
                                Right = new SqlNullExpression()
                            },
                            Value = new SqlNullExpression()
                        },
                    },
                    Else = new SqlSelectExpression()
                    {
                        Query = new SqlSelectQueryExpression()
                        {
                            Columns = new List<SqlSelectItemExpression>()
                            {
                                new SqlSelectItemExpression()
                                {
                                    Body = new SqlPropertyExpression()
                                    {
                                        Name = new SqlIdentifierExpression()
                                        {
                                            Value = "name",
                                        },
                                        Table = new SqlIdentifierExpression()
                                        {
                                            Value = "a",
                                        },
                                    },
                                    Alias = new SqlIdentifierExpression()
                                    {
                                        Value = "area_name",
                                    },
                                },
                            },
                            From = new SqlTableExpression()
                            {
                                Name = new SqlIdentifierExpression()
                                {
                                    Value = "portal_area",
                                },
                                Alias = new SqlIdentifierExpression()
                                {
                                    Value = "a",
                                },
                            },
                            Where = new SqlBinaryExpression()
                            {
                                Left = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "id",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "a",
                                    },
                                },
                                Operator = SqlBinaryOperator.EqualTo,
                                Right = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "area_id",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "c",
                                    },
                                },
                            },
                        },
                    },
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "area_name",
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "portal_company",
            },
            Schema = new SqlIdentifierExpression()
            {
                Value = "portal",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "c",
            },
        },
    },
};


在这个例子中,要查询的列值为一个SqlSelectExpression表达式,即要查询的列是一个子查询

1.2 Where条件过滤语句

1.2.1 二元表达式

var sql = "SELECT * FROM test WHERE ID =1";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
        },
        Where = new SqlBinaryExpression()
        {
            Left = new SqlIdentifierExpression()
            {
                Value = "ID",
            },
            Operator = SqlBinaryOperator.EqualTo,
            Right = new SqlNumberExpression()
            {
                Value = 1M,
            },
        },
    },
};


在这个例子中,where字段的值是一个二元表达式SqlBinaryExpression,他包含了

  1. 左边部分,即Left字段,值为一个SqlIdentifierExpression,即标识符表达式,它的值为ID
  2. 右边部分,即Right字段,值为一个SqlNumberExpression,即数字表达式,它的值为1
  3. 中间符号,即Operator字段,值为EqualTo,即等号,当然了,还可以是大于号,小于号,大于等于号,小于等于号,不等号等

二元表达式的两边可以非常灵活,可以是各种其他表达式,同时也可以自我嵌套另一个二元表达式,组成一个非常复杂的二元表达式

1.2.2 between/not between子句

var sql = "SELECT * FROM test WHERE ID BETWEEN 1 AND 2";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
        },
        Where = new SqlBetweenAndExpression()
        {
            Body = new SqlIdentifierExpression()
            {
                Value = "ID",
            },
            Begin = new SqlNumberExpression()
            {
                Value = 1M,
            },
            End = new SqlNumberExpression()
            {
                Value = 2M,
            },
        },
    },
};


between子句包含了

  1. Begin部分,即Begin字段,在这个例子中,值为一个SqlNumberExpression,,它的值为1
  2. End部分,即End字段,在这个例子中,值为一个SqlNumberExpression,它的值为2
  3. Body主体部分,即Body字段,值为SqlIdentifierExpression,即标识符表达式,值为id
  4. 取反部分,即IsNot字段,如果是not between,则IsNot=true

1.2.3 is null/is not null子句

var sql = "select * from test rd where rd.name is null";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "rd",
            },
        },
        Where = new SqlBinaryExpression()
        {
            Left = new SqlPropertyExpression()
            {
                Name = new SqlIdentifierExpression()
                {
                    Value = "name",
                },
                Table = new SqlIdentifierExpression()
                {
                    Value = "rd",
                },
            },
            Operator = SqlBinaryOperator.Is,
            Right = new SqlNullExpression()
        },
    },
};



is null/is not null子句主要体现在二元表达式里,Operator字段为Is/IsNot,right字段为SqlNullExpression,即null表达式,代表值为null

1.2.4 exists/not exists子句

var sql = "select * from TEST t where EXISTS(select * from TEST2 t2)";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
        Where = new SqlExistsExpression()
        {
            Body = new SqlSelectExpression()
            {
                Query = new SqlSelectQueryExpression()
                {
                    Columns = new List<SqlSelectItemExpression>()
                    {
                        new SqlSelectItemExpression()
                        {
                            Body = new SqlAllColumnExpression()
                        },
                    },
                    From = new SqlTableExpression()
                    {
                        Name = new SqlIdentifierExpression()
                        {
                            Value = "TEST2",
                        },
                        Alias = new SqlIdentifierExpression()
                        {
                            Value = "t2",
                        },
                    },
                },
            },
        },
    },
};




exists/not exists子句,主要体现为SqlExistsExpression表达式,

  1. 主体,即body字段,本例子中值为一个SqlSelectExpression表达式
  2. 取反部分,即IsNot字段,如果是not exists,则IsNot=true

1.2.5 like/not like子句

var sql = "SELECT * from TEST t WHERE name LIKE '%a%'";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
        Where = new SqlBinaryExpression()
        {
            Left = new SqlIdentifierExpression()
            {
                Value = "name",
            },
            Operator = SqlBinaryOperator.Like,
            Right = new SqlStringExpression()
            {
                Value = "%a%"
            },
        },
    },
};


like子句,主要体现在二元表达式里,Operator字段为Like/NotLike,本例子中right字段为字符串表达式,即SqlStringExpression表达式,值为%a%。

1.2.6 all/any子句

var sql = "select * from customer c where c.Age >all(select o.Quantity  from orderdetail o)";
var sqlAst = DbUtils.Parse(sql, DbType.MySql);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "customer",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "c",
            },
        },
        Where = new SqlBinaryExpression()
        {
            Left = new SqlPropertyExpression()
            {
                Name = new SqlIdentifierExpression()
                {
                    Value = "Age",
                },
                Table = new SqlIdentifierExpression()
                {
                    Value = "c",
                },
            },
            Operator = SqlBinaryOperator.GreaterThen,
            Right = new SqlAllExpression()
            {
                Body = new SqlSelectExpression()
                {
                    Query = new SqlSelectQueryExpression()
                    {
                        Columns = new List<SqlSelectItemExpression>()
                        {
                            new SqlSelectItemExpression()
                            {
                                Body = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "Quantity",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "o",
                                    },
                                },
                            },
                        },
                        From = new SqlTableExpression()
                        {
                            Name = new SqlIdentifierExpression()
                            {
                                Value = "orderdetail",
                            },
                            Alias = new SqlIdentifierExpression()
                            {
                                Value = "o",
                            },
                        },
                    },
                },
            },
        },
    },
};



all/any子句,主要体现在SqlAllExpression/SqlAnyExpression表达式,它的body里是另一个SqlSelectExpression表达式

1.2.7 in/ not in子句

var sql = "SELECT  * from TEST t WHERE t.NAME IN ('a','b','c')";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
        Where = new SqlInExpression()
        {
            Body = new SqlPropertyExpression()
            {
                Name = new SqlIdentifierExpression()
                {
                    Value = "NAME",
                },
                Table = new SqlIdentifierExpression()
                {
                    Value = "t",
                },
            },
            TargetList = new List<SqlExpression>()
            {
                new SqlStringExpression()
                {
                    Value = "a"
                },
                new SqlStringExpression()
                {
                    Value = "b"
                },
                new SqlStringExpression()
                {
                    Value = "c"
                },
            },
        },
    },
};



in/not in子句,主要体现在SqlInExpression表达式,它包含了

  1. body字段,即in的主体,在这里是SqlPropertyExpression,值为t.NAME
  2. TargetList字段,即in的目标列表,在这里是一个SqlExpression的列表,里面包括3个SqlStringExpression,即字符串表达式,a,b,c
  3. 取反部分,即IsNot字段,如果是not in,则IsNot=true

当然了,in也有另一种子查询的类型,即

var sql = "select * from TEST5  WHERE NAME IN (SELECT NAME  FROM TEST3)";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST5",
            },
        },
        Where = new SqlInExpression()
        {
            Body = new SqlIdentifierExpression()
            {
                Value = "NAME",
            },
            SubQuery = new SqlSelectExpression()
            {
                Query = new SqlSelectQueryExpression()
                {
                    Columns = new List<SqlSelectItemExpression>()
                    {
                        new SqlSelectItemExpression()
                        {
                            Body = new SqlIdentifierExpression()
                            {
                                Value = "NAME",
                            },
                        },
                    },
                    From = new SqlTableExpression()
                    {
                        Name = new SqlIdentifierExpression()
                        {
                            Value = "TEST3",
                        },
                    },
                },
            },
        },
    },
};

在这里的SqlInExpression表达式中,它包含了

  1. body字段,即in的主体,在这里是SqlIdentifierExpression,值为NAME
  2. SubQuery字段,即子查询,值为一个SqlSelectExpression
  3. IsNot字段,如果是not in,则IsNot=true

1.2.8 case when子句

var sql = "SELECT CASE WHEN t.name='1' THEN 'a' WHEN t.name='2' THEN 'b' ELSE 'c' END AS v from TEST t";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlCaseExpression()
                {
                    Items = new List<SqlCaseItemExpression>()
                    {
                        new SqlCaseItemExpression()
                        {
                            Condition = new SqlBinaryExpression()
                            {
                                Left = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "name",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "t",
                                    },
                                },
                                Operator = SqlBinaryOperator.EqualTo,
                                Right = new SqlStringExpression()
                                {
                                    Value = "1"
                                },
                            },
                            Value = new SqlStringExpression()
                            {
                                Value = "a"
                            },
                        },
                        new SqlCaseItemExpression()
                        {
                            Condition = new SqlBinaryExpression()
                            {
                                Left = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "name",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "t",
                                    },
                                },
                                Operator = SqlBinaryOperator.EqualTo,
                                Right = new SqlStringExpression()
                                {
                                    Value = "2"
                                },
                            },
                            Value = new SqlStringExpression()
                            {
                                Value = "b"
                            },
                        },
                    },
                    Else = new SqlStringExpression()
                    {
                        Value = "c"
                    },
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "v",
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
    },
};



case when子句,主要体现在SqlCaseExpression表达式里,他包含了

  1. 各种case when键值对的列表,即Items字段,列表里的每一个元素都是SqlCaseItemExpression表达式,SqlCaseItemExpression表达式,又包含了
    1. 条件,即Condition字段,在本例子中是二元表达式,即SqlBinaryExpression表达式,值为t.name =’1′
    2. 值,即value字段,在本例子中值为字符串a
  2. Else字段,即默认值,本例子中为字符串c

case when还有另外一种句式,如下:

var sql = "select case t.name when 'a' then 1 else 2 end  from test t ";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlCaseExpression()
                {
                    Items = new List<SqlCaseItemExpression>()
                    {
                        new SqlCaseItemExpression()
                        {
                            Condition = new SqlStringExpression()
                            {
                                Value = "a"
                            },
                            Value = new SqlNumberExpression()
                            {
                                Value = 1M,
                            },
                        },
                    },
                    Else = new SqlNumberExpression()
                    {
                        Value = 2M,
                    },
                    Value = new SqlPropertyExpression()
                    {
                        Name = new SqlIdentifierExpression()
                        {
                            Value = "name",
                        },
                        Table = new SqlIdentifierExpression()
                        {
                            Value = "t",
                        },
                    },
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
    },
};



在这种SqlCaseExpression表达式里,他包含了

  1. case条件的主体变量,即Value字段,本例子中值为SqlPropertyExpression,它的值为t.name
  2. 各种when then键值对的列表,即Items字段,列表里的每一个元素都是SqlCaseItemExpression表达式,SqlCaseItemExpression表达式,又包含了
    1. 条件,即Condition字段,在本例子中是字符串表达式SqlStringExpression,它的值为a
    2. 值,即Value字段,在本例子中值为SqlNumberExpression,它的值为1
  3. Else字段,即默认值,本例子中为数字2

1.2.9 not子句

var sql = "select * from TEST t WHERE not t.NAME ='abc'";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
        Where = new SqlNotExpression()
        {
            Body = new SqlBinaryExpression()
            {
                Left = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "NAME",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "t",
                    },
                },
                Operator = SqlBinaryOperator.EqualTo,
                Right = new SqlStringExpression()
                {
                    Value = "abc"
                },
            },
        },
    },
};



not子句,主要体现在SqlNotExpression表达式里,它只有一个body字段,即代表否定的部分

1.2.10 变量子句

var sql = "select * from TEST t WHERE not t.NAME =:name";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
        Where = new SqlNotExpression()
        {
            Body = new SqlBinaryExpression()
            {
                Left = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "NAME",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "t",
                    },
                },
                Operator = SqlBinaryOperator.EqualTo,
                Right = new SqlVariableExpression()
                {
                    Name = "name",
                    Prefix = ":",
                },
            },
        },
    },
};


变量子句,主要体现在SqlVariableExpression表达式里,它包括以下部分:

  1. 变量名,即字段Name,这里值为name
  2. 变量前缀,这里值为:

1.3 From数据源

在sql中,From关键字后面有多种形式来指定数据源。主要有以下几种

1.3.1 表名或者视图

select * from test

这个解析结果上面已经演示了。

1.3.2 子查询(子表)

var sql = "select * from (select * from test) t";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlSelectExpression()
        {
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
            Query = new SqlSelectQueryExpression()
            {
                Columns = new List<SqlSelectItemExpression>()
                {
                    new SqlSelectItemExpression()
                    {
                        Body = new SqlAllColumnExpression()
                    },
                },
                From = new SqlTableExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "test",
                    },
                },
            },
        },
    },
};

在这个例子中,数据源From的值为一个SqlSelectExpression,即SqlSelectExpression中可以嵌套SqlSelectExpression,同时我们注意到内部的SqlSelectExpression有一个表别名的字段Alias,标识符的值为t,表示表别名为t;

1.3.3 连表查询(JOIN)

var sql = "select t1.id from test t1 left join test2 t2 on t1.id=t2.id right join test3 t3 on t2.id=t3.id";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "id",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "t1",
                    },
                },
            },
        },
        From = new SqlJoinTableExpression()
        {
            Left = new SqlJoinTableExpression()
            {
                Left = new SqlTableExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "test",
                    },
                    Alias = new SqlIdentifierExpression()
                    {
                        Value = "t1",
                    },
                },
                JoinType = SqlJoinType.LeftJoin,
                Right = new SqlTableExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "test2",
                    },
                    Alias = new SqlIdentifierExpression()
                    {
                        Value = "t2",
                    },
                },
                Conditions = new SqlBinaryExpression()
                {
                    Left = new SqlPropertyExpression()
                    {
                        Name = new SqlIdentifierExpression()
                        {
                            Value = "id",
                        },
                        Table = new SqlIdentifierExpression()
                        {
                            Value = "t1",
                        },
                    },
                    Operator = SqlBinaryOperator.EqualTo,
                    Right = new SqlPropertyExpression()
                    {
                        Name = new SqlIdentifierExpression()
                        {
                            Value = "id",
                        },
                        Table = new SqlIdentifierExpression()
                        {
                            Value = "t2",
                        },
                    },
                },
            },
            JoinType = SqlJoinType.RightJoin,
            Right = new SqlTableExpression()
            {
                Name = new SqlIdentifierExpression()
                {
                    Value = "test3",
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "t3",
                },
            },
            Conditions = new SqlBinaryExpression()
            {
                Left = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "id",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "t2",
                    },
                },
                Operator = SqlBinaryOperator.EqualTo,
                Right = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "id",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "t3",
                    },
                },
            },
        },
    },
};

在上面这个例子中,我们演示了连表查询是如何解析的,From字段的值为一个SqlJoinTableExpression,即连表查询表达式,他包含了

  1. 左边部分,即Left字段
  2. 右边部分,即Right字段
  3. 连接方式,即JoinType字段,值包括InnerJoin,LeftJoin,RightJoin,FullJoin,CrossJoin,CommaJoin
  4. 表关联条件,即Conditions字段。在这里,Conditions字段的值为一个二元表达式SqlBinaryExpression

在这个例子中,总共3张表联查,SqlJoinTableExpression中得left字段又是一个SqlJoinTableExpression,即SqlJoinTableExpression中可以嵌套SqlJoinTableExpression,无限套娃。

1.3.4 公用表表达式(CTE)

var sql = "with c1 as (select name from test t) , c2(name) AS (SELECT name FROM TEST2 t3 ) select *from c1 JOIN c2 ON c1.name=c2.name";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        WithSubQuerys = new List<SqlWithSubQueryExpression>()
        {
            new SqlWithSubQueryExpression()
            {
                Alias = new SqlIdentifierExpression()
                {
                    Value = "c1",
                },
                FromSelect = new SqlSelectExpression()
                {
                    Query = new SqlSelectQueryExpression()
                    {
                        Columns = new List<SqlSelectItemExpression>()
                        {
                            new SqlSelectItemExpression()
                            {
                                Body = new SqlIdentifierExpression()
                                {
                                    Value = "name",
                                },
                            },
                        },
                        From = new SqlTableExpression()
                        {
                            Name = new SqlIdentifierExpression()
                            {
                                Value = "test",
                            },
                            Alias = new SqlIdentifierExpression()
                            {
                                Value = "t",
                            },
                        },
                    },
                },
            },
            new SqlWithSubQueryExpression()
            {
                Alias = new SqlIdentifierExpression()
                {
                    Value = "c2",
                },
                FromSelect = new SqlSelectExpression()
                {
                    Query = new SqlSelectQueryExpression()
                    {
                        Columns = new List<SqlSelectItemExpression>()
                        {
                            new SqlSelectItemExpression()
                            {
                                Body = new SqlIdentifierExpression()
                                {
                                    Value = "name",
                                },
                            },
                        },
                        From = new SqlTableExpression()
                        {
                            Name = new SqlIdentifierExpression()
                            {
                                Value = "TEST2",
                            },
                            Alias = new SqlIdentifierExpression()
                            {
                                Value = "t3",
                            },
                        },
                    },
                },
                Columns = new List<SqlIdentifierExpression>()
                {
                    new SqlIdentifierExpression()
                    {
                        Value = "name",
                    },
                },
            },
        },
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlJoinTableExpression()
        {
            Left = new SqlTableExpression()
            {
                Name = new SqlIdentifierExpression()
                {
                    Value = "c1",
                },
            },
            JoinType = SqlJoinType.InnerJoin,
            Right = new SqlTableExpression()
            {
                Name = new SqlIdentifierExpression()
                {
                    Value = "c2",
                },
            },
            Conditions = new SqlBinaryExpression()
            {
                Left = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "name",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "c1",
                    },
                },
                Operator = SqlBinaryOperator.EqualTo,
                Right = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "name",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "c2",
                    },
                },
            },
        },
    },
};

公用表表达式(CTE),主要体现在SqlSelectQueryExpression的WithSubQuerys字段,他是一个SqlWithSubQueryExpression表达式列表,即公用表列表,它里面的每一个元素都是SqlWithSubQueryExpression表达式,此表达式,包含了

  1. 公共表的来源部分,即FromSelect字段,在本例子中,他的值是一个SqlSelectExpression表达式,即一个查询
  2. 公共表的表别名,即Alias字段,在本例子中,他的值是c1
  3. 公共表的列部分,即Columns字段,在本例子中只有一个列名,即name

1.3.5 函数返回的结果集

特定数据库支持从返回结果集的函数中查询,比如oracle中添加一个自定义函数splitstr,他的作用是将一个字符串根据;号进行分割,返回多行数据

var sql = "SELECT * FROM TABLE(splitstr('a;b',';'))";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlReferenceTableExpression()
        {
            FunctionCall = new SqlFunctionCallExpression()
            {
                Name = new SqlIdentifierExpression()
                {
                    Value = "TABLE",
                },
                Arguments = new List<SqlExpression>()
                {
                    new SqlFunctionCallExpression()
                    {
                        Name = new SqlIdentifierExpression()
                        {
                            Value = "splitstr",
                        },
                        Arguments = new List<SqlExpression>()
                        {
                            new SqlStringExpression()
                            {
                                Value = "a;b"
                            },
                            new SqlStringExpression()
                            {
                                Value = ";"
                            },
                        },
                    },
                },
            },
        }
    },
};

函数返回的结果集主要体现在SqlReferenceTableExpression表达式,他的内部包含了一个FunctionCall字段,值为SqlFunctionCallExpression表达式,代表从函数调用的结果集中进行查询。

1.4 OrderBy排序语句

var sql = "select fa.FlowId  from FlowActivity fa order by fa.FlowId desc,fa.Id asc";
var sqlAst = DbUtils.Parse(sql, DbType.SqlServer);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "FlowId"
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "fa"
                    },
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "FlowActivity"
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "fa"
            },
        },
        OrderBy = new SqlOrderByExpression()
        {
            Items = new List<SqlOrderByItemExpression>()
            {
                new SqlOrderByItemExpression()
                {
                    Body =
                        new SqlPropertyExpression()
                        {
                            Name = new SqlIdentifierExpression()
                            {
                                Value = "FlowId"
                            },
                            Table = new SqlIdentifierExpression()
                            {
                                Value = "fa"
                            },
                        },
                    OrderByType = SqlOrderByType.Desc
                },
                new SqlOrderByItemExpression()
                {
                    Body =
                        new SqlPropertyExpression()
                        {
                            Name = new SqlIdentifierExpression()
                            {
                                Value = "Id"
                            },
                            Table = new SqlIdentifierExpression()
                            {
                                Value = "fa"
                            },
                        },
                    OrderByType = SqlOrderByType.Asc
                },
            },
        },
    },
};


OrderBy排序子句,值为SqlOrderByExpression表达式,表达式的内容也非常简单,只有一个Items,即一个排序子项表达式的列表,列表里的值为SqlOrderByItemExpression,即排序子项表达式,排序子项表达式里又包含了以下内容

  1. 排序依据,即Body字段,在这个例子中,排序依据是2个SqlPropertyExpression表达式,即根据fa.FlowId,fa.Id排序
  2. 排序类型,即OrderByType字段,值为Asc或者Desc,默认为asc,在这2个例子中,有asc和Desc
  3. 决定null排在前面或后面的NullsType字段,在oracle,pgsql,sqlite中我们可以指定null在排序中的位置,如以下sql
select * from TEST5 t order by t.NAME  desc nulls FIRST,t.AGE ASC NULLS  last 

那么我们的NullsType字段,他的值有SqlOrderByNullsType.First和SqlOrderByNullsType.Last,与之对应。

1.5 GroupBy分组语句

var sql = "select fa.FlowId  from FlowActivity fa group by fa.FlowId,fa.Id HAVING count(fa.Id)>1";
var sqlAst = DbUtils.Parse(sql, DbType.SqlServer);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "FlowId"
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "fa"
                    },
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "FlowActivity"
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "fa"
            },
        },
        GroupBy = new SqlGroupByExpression()
        {
            Items = new List<SqlExpression>()
            {
                new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "FlowId"
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "fa"
                    },
                },
                new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "Id"
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "fa"
                    },
                },
            },
            Having = new SqlBinaryExpression()
            {
                Left = new SqlFunctionCallExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "count"
                    },
                    Arguments = new List<SqlExpression>()
                    {
                        new SqlPropertyExpression()
                        {
                            Name = new SqlIdentifierExpression()
                            {
                                Value = "Id"
                            },
                            Table = new SqlIdentifierExpression()
                            {
                                Value = "fa"
                            },
                        },
                    },
                },
                Operator = SqlBinaryOperator.GreaterThen,
                Right = new SqlNumberExpression()
                {
                    Value = 1M
                },
            },
        },
    },
};


GroupBy分组语句,值为SqlGroupByExpression表达式,他的内容如下

  1. 分组子项表达式的列表,即Items字段,列表里的值为SqlExpression,他的值是一个逻辑子句
  2. 分组过滤子句,即Having字段,他的值是一个逻辑子句,在本例子中,逻辑子句的值为一个SqlBinaryExpression

1.5 Limit分页子句

1.5.1 mysql,sqlite

var sql = "select * from test t limit 1,5";
var sqlAst = DbUtils.Parse(sql, DbType.MySql);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
        Limit = new SqlLimitExpression()
        {
            Offset = new SqlNumberExpression()
            {
                Value = 1M,
            },
            RowCount = new SqlNumberExpression()
            {
                Value = 5M,
            },
        },
    },
};


Limit分页子句,值为SqlLimitExpression表达式,他的内容如下

  1. 每页数量,即RowCount字段,这本例子中,值为5
  2. 跳过数量,即Offset字段,本例子中,值为1

1.5.2 oracle

var sql = "SELECT * FROM TEST3 t  ORDER BY t.NAME  DESC FETCH FIRST 2 rows ONLY";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            }
        },
        From = new SqlTableExpression()
        {
            Alias = new SqlIdentifierExpression()
            {
                Value = "t"
            },
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST3"
            }
        },

        OrderBy = new SqlOrderByExpression()
        {
            Items = new List<SqlOrderByItemExpression>()
            {
                new SqlOrderByItemExpression()
                {
                    OrderByType = SqlOrderByType.Desc,
                    Body = new SqlPropertyExpression()
                    {
                        Name = new SqlIdentifierExpression() { Value = "NAME" },
                        Table = new SqlIdentifierExpression()
                        {
                            Value = "t"
                        }
                    }
                }
            }
        },
        Limit = new SqlLimitExpression()
        {
            RowCount = new SqlNumberExpression()
            {
                Value = 2
            }
        }
    }
};

1.5.3 pgsql

var sql = "select * from test5   t order by t.name limit 1 offset 10;";
var sqlAst = DbUtils.Parse(sql, DbType.Pgsql);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test5",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
        OrderBy = new SqlOrderByExpression()
        {
            Items = new List<SqlOrderByItemExpression>()
            {
                new SqlOrderByItemExpression()
                {
                    Body = new SqlPropertyExpression()
                    {
                        Name = new SqlIdentifierExpression()
                        {
                            Value = "name",
                        },
                        Table = new SqlIdentifierExpression()
                        {
                            Value = "t",
                        },
                    },
                },
            },
        },
        Limit = new SqlLimitExpression()
        {
            Offset = new SqlNumberExpression()
            {
                Value = 10M,
            },
            RowCount = new SqlNumberExpression()
            {
                Value = 1M,
            },
        },
    },
};


1.5.4 SQLServer

var sql = "select * from test t order by t.name OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY";
var sqlAst = DbUtils.Parse(sql, DbType.SqlServer);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            }
        },
        From = new SqlTableExpression()
        {
            Alias = new SqlIdentifierExpression()
            {
                Value = "t"
            },
            Name = new SqlIdentifierExpression()
            {
                Value = "test"
            }
        },

        OrderBy = new SqlOrderByExpression()
        {
            Items = new List<SqlOrderByItemExpression>()
            {
                new SqlOrderByItemExpression()
                {
                    Body = new SqlPropertyExpression()
                    {
                        Name = new SqlIdentifierExpression() { Value = "name" },
                        Table = new SqlIdentifierExpression()
                        {
                            Value = "t"
                        }
                    }
                }
            }
        },
        Limit = new SqlLimitExpression()
        {
            Offset = new SqlNumberExpression()
            {
                Value = 5
            },
            RowCount = new SqlNumberExpression()
            {
                Value = 10
            }
        }
    }
};


1.6 ConnectBy层次查询语句(oracle专用)

var sql = "SELECT EMPLOYEEID , MANAGERID , LEVEL FROM EMPLOYEE e START WITH MANAGERID IS NULL CONNECT BY NOCYCLE PRIOR EMPLOYEEID = MANAGERID ORDER SIBLINGS BY EMPLOYEEID ";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlIdentifierExpression()
                {
                    Value = "EMPLOYEEID",
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlIdentifierExpression()
                {
                    Value = "MANAGERID",
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlIdentifierExpression()
                {
                    Value = "LEVEL",
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "EMPLOYEE",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "e",
            },
        },
        ConnectBy = new SqlConnectByExpression()
        {
            StartWith = new SqlBinaryExpression()
            {
                Left = new SqlIdentifierExpression()
                {
                    Value = "MANAGERID",
                },
                Operator = SqlBinaryOperator.Is,
                Right = new SqlNullExpression()
            },
            Body = new SqlBinaryExpression()
            {
                Left = new SqlIdentifierExpression()
                {
                    Value = "EMPLOYEEID",
                },
                Operator = SqlBinaryOperator.EqualTo,
                Right = new SqlIdentifierExpression()
                {
                    Value = "MANAGERID",
                },
            },
            IsNocycle = true,
            IsPrior = true,
            OrderBy = new SqlOrderByExpression()
            {
                Items = new List<SqlOrderByItemExpression>()
                {
                    new SqlOrderByItemExpression()
                    {
                        Body = new SqlIdentifierExpression()
                        {
                            Value = "EMPLOYEEID",
                        },
                    },
                },
                IsSiblings = true,
            },
        },
    },
};

ConnectBy层次查询子句,值为SqlConnectByExpression表达式,他的内容如下

  1. 指定层次查询的根节点条件,即StartWith字段,本例子中他的值为SqlBinaryExpression二元表达式
  2. 主体关联条件子句,即Body字段,本例子中他的值是一个SqlBinaryExpression二元表达式
  3. IsPrior字段用来指示层次结构中哪个列是父节点,如果sql中存在Prior关键字,则值为true
  4. IsNocycle字段,用来防止循环引用导致无限递归,如果sql中存在Nocycle则为true
  5. order by子句,用于排序

1.7 Into子句(sql server专用)

var sql = "SELECT name into test14 from TEST as t ";
var sqlAst = DbUtils.Parse(sql, DbType.SqlServer);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlIdentifierExpression()
                {
                    Value = "name"
                },
            },
        },
        Into = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test14"
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST"
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t"
            },
        },
    },
};

into子句,在本例子中值为SqlTableExpression,即into到某张表里。

2. Insert插入语句

2.1 插入单个值

var sql = "insert into test11(name,id) values('a1','a2')";
var sqlAst = DbUtils.Parse(sql, DbType.SqlServer);

解析结果如下:

var expect = new SqlInsertExpression()
{
    Columns = new List<SqlExpression>()
    {
        new SqlIdentifierExpression()
        {
            Value = "name"
        },
        new SqlIdentifierExpression()
        {
            Value = "id"
        },
    },
    ValuesList = new List<List<SqlExpression>>()
    {
        new List<SqlExpression>()
        {
            new SqlStringExpression()
            {
                Value = "a1"
            },
            new SqlStringExpression()
            {
                Value = "a2"
            },
        },
    },
    Table = new SqlTableExpression()
    {
        Name = new SqlIdentifierExpression()
        {
            Value = "test11"
        },
    },
};

如上例子,插入语句表现为一个SqlInsertExpression,他包含了

  1. 要插入的字段列表,即Columns字段,值为一个SqlExpression的列表,本例子中值为2个SqlIdentifierExpression,它们的值为name和id,即插入name和id字段
  2. 值列表,即ValuesList字段,值为一个List<SqlExpression>的列表,即列表里的元素是一个列表,每个元素代表一组待插入的数据,本例子中列表里只有一个List,并且子列表里的值为a1和a2,即待插入的值为a1和a2。
  3. 待插入数据的表,即Table字段,本例子中值为test11

为什么ValuesList字段是列表里嵌套列表呢?主要是因为可以插入多个值列表,让我们继续往下看

2.2 插入多个值

var sql = "insert into test11(name,id) values('a1','a2'),('a3','a4')";
var sqlAst = DbUtils.Parse(sql, DbType.MySql);

解析结果如下:

var expect = new SqlInsertExpression()
{
    Columns = new List<SqlExpression>()
    {
        new SqlIdentifierExpression()
        {
            Value = "name"
        },
        new SqlIdentifierExpression()
        {
            Value = "id"
        },
    },
    ValuesList = new List<List<SqlExpression>>()
    {
        new List<SqlExpression>()
        {
            new SqlStringExpression()
            {
                Value = "a1"
            },
            new SqlStringExpression()
            {
                Value = "a2"
            },
        },
        new List<SqlExpression>()
        {
            new SqlStringExpression()
            {
                Value = "a3"
            },
            new SqlStringExpression()
            {
                Value = "a4"
            },
        },
    },
    Table = new SqlTableExpression()
    {
        Name = new SqlIdentifierExpression()
        {
            Value = "test11"
        },
    },
};

在本例子中,ValuesList字段中有2个子元素,即2个List列表,代表插入2组数据,值分别为(‘a1′,’a2’)和(‘a3′,’a4’)

2.3 待插入的值为一个子查询

var sql = "INSERT INTO TEST2(name) SELECT name AS name2 FROM TEST t";
var sqlAst = DbUtils.Parse(sql, DbType.MySql);

解析结果如下:

var expect = new SqlInsertExpression()
{
    Columns = new List<SqlExpression>()
    {
        new SqlIdentifierExpression()
        {
            Value = "name"
        },
    },
    Table = new SqlTableExpression()
    {
        Name = new SqlIdentifierExpression()
        {
            Value = "TEST2"
        },
    },
    FromSelect = new SqlSelectExpression()
    {
        Query = new SqlSelectQueryExpression()
        {
            Columns = new List<SqlSelectItemExpression>()
            {
                new SqlSelectItemExpression()
                {
                    Body = new SqlIdentifierExpression()
                    {
                        Value = "name"
                    },
                    Alias = new SqlIdentifierExpression()
                    {
                        Value = "name2"
                    },
                },
            },
            From = new SqlTableExpression()
            {
                Name = new SqlIdentifierExpression()
                {
                    Value = "TEST"
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "t"
                },
            },
        },
    },
};

如上例子,插入语句表现为一个SqlInsertExpression,他包含了

  1. 要插入的字段列表,即Columns字段,值为一个SqlExpression的列表,本例子中值为name
  2. 子查询来源,即FromSelect字段,值为一个SqlSelectExpression,即一个子查询
  3. 待插入数据的表,即Table字段,本例子中值为TEST2

3. Update更新语句

var sql = "update test set name ='4',d='2024-11-22 08:19:47.243' where name ='1'";
var sqlAst = DbUtils.Parse(sql, DbType.MySql);

解析结果如下:

var expect = new SqlUpdateExpression()
{
    Table = new SqlTableExpression()
    {
        Name = new SqlIdentifierExpression()
        {
            Value = "test"
        },
    },
    Where = new SqlBinaryExpression()
    {
        Left = new SqlIdentifierExpression()
        {
            Value = "name"
        },
        Operator = SqlBinaryOperator.EqualTo,
        Right = new SqlStringExpression()
        {
            Value = "1"
        },
    },
    Items = new List<SqlExpression>()
    {
        new SqlBinaryExpression()
        {
            Left = new SqlIdentifierExpression()
            {
                Value = "name"
            },
            Operator = SqlBinaryOperator.EqualTo,
            Right = new SqlStringExpression()
            {
                Value = "4"
            },
        },
        new SqlBinaryExpression()
        {
            Left = new SqlIdentifierExpression()
            {
                Value = "d"
            },
            Operator = SqlBinaryOperator.EqualTo,
            Right = new SqlStringExpression()
            {
                Value = "2024-11-22 08:19:47.243"
            },
        },
    },
};

如上例子,更新语句表现为一个SqlUpdateExpression,他包含了

  1. 要更新的(字段-值)的列表,即Items字段,值为一个SqlExpression的列表,本例子中值为2个SqlBinaryExpression,即name=’4’和d=’2024-11-22 08:19:47.243′
  2. 条件过滤子句,即Where字段,代表过滤条件,本例子中值为一个SqlBinaryExpression,即name =’1′
  3. 待更新数据的表,即Table字段,本例子中值为test

4. Delete删除语句

var sql = "delete from test where name=4";
var sqlAst = DbUtils.Parse(sql, DbType.MySql);

解析结果如下:

var expect = new SqlDeleteExpression()
{
    Table = new SqlTableExpression()
    {
        Name = new SqlIdentifierExpression()
        {
            Value = "test"
        },
    },
    Where = new SqlBinaryExpression()
    {
        Left = new SqlIdentifierExpression()
        {
            Value = "name"
        },
        Operator = SqlBinaryOperator.EqualTo,
        Right = new SqlNumberExpression()
        {
            Value = 4M
        },
    },
};

如上例子,删除语句表现为一个SqlDeleteExpression,他包含了

  1. 条件过滤子句,即Where字段,代表过滤条件,本例子中值为一个SqlBinaryExpression,即name=4
  2. 待删除数据的表,即Table字段,本例子中值为test

5. 注释处理

5.1 单行注释

var sql = @"select *--abc from test lbu WHERE a ='1'--aaaaaa
FROM test";
var sqlAst = DbUtils.Parse(sql, DbType.SqlServer);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
        },
    },
};

如上例子,单行注释被正确忽视,解析正确。

5.2 多行注释

var sql = @"/*这
            是
            顶部*/
            select *--abc
            FROM test/*这
            是
            底部*/";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
        },
    },
};

如上例子,多行注释被正确忽视,解析正确。

6. 如何解析ast抽象语法树

当我们通过

var sql = @"select * from test";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析sql获取到抽象语法树以后,我们就要对这颗抽象语法树进行解析,获取我们想要的数据,此时就要用上访问者模式(visitor) .

6.1 访问者模式

访问者模式最大的特点就是结构与算法分离,结合本项目理解,就是ast抽象语法树这个结构已经解析出来了,你可以根据自己的需要写算法去任意解析这颗语法树。这是一个1-N的操作,即一个抽象语法树,可以对应N个解析算法,当我们要自定义算法去解析抽象语法树时,我们需要自定义一个Visitor类,并且实现IAstVisitor接口

public class CustomVisitor : IAcceptVisitor
{
    
}

但是实现这个接口要实现接口里的很多个方法,并且有些数据并不是我们关心的,所以我提供了一个实现了IAcceptVisitor接口的抽象类BaseAstVisitor用来简化操作,我们只需要继承这个抽象类,然后重写我们感兴趣的方法即可

public class CustomVisitor : BaseAstVisitor
{
    
}

在本项目中,我提供了2个基本的vistor供大家使用,UnitTestAstVisitor和SqlGenerationAstVisitor,大家可以参考这2个visitor去写自己的算法来解析抽象语法树。接下来,我将介绍这2个visitor的用法。

6.2 UnitTestAstVisitor

当我们通过

var sql = @"select * from test";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析sql获取到抽象语法树之后,sqlAst其实还是一个数据结构,我们可以通过vs监视这个变量来查看内部的结构,但是如果是非常复杂的sql,那这颗树会巨大无比,要靠我们手动去慢慢点开查看得累死,没错!写单元测试的时候,我刚开始都是用手写的结果去对比引擎解析出来的结果,后来我就被累死了,该说不说,这活真不是人干的,所以痛定思痛之后我就写了这个UnitTestAstVisitor来替我生成ast的结构字符串,接下来让我们看看用法

var sql = @"select * from test";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);
var unitTestAstVisitor = new UnitTestAstVisitor();
sqlAst.Accept(unitTestAstVisitor);
var result = unitTestAstVisitor.GetResult();

其中的result就是解析抽象语法树生成的字符串,如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
        },
    },
};

然后把这个生成的字符串黏贴到vs里去和引擎生成的结果进行对比,

Assert.True(sqlAst.Equals(expect));

至此,我写单元测试的工作量大大减轻,同时对于生成的sqlAst语法树的结构也更加一目了然了。

6.2 SqlGenerationAstVisitor

我们通过解析sql生成了抽象语法树之后,如果我们想要给这颗抽象语法树添加一个where条件,比如添加test.name =’a’

var sql = @"select * from test";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);
if (sqlAst is SqlSelectExpression sqlSelectExpression && sqlSelectExpression.Query is SqlSelectQueryExpression sqlSelectQueryExpression)
{
    sqlSelectQueryExpression.Where = new SqlBinaryExpression()
    {
        Left = new SqlPropertyExpression()
        {
            Table = new SqlIdentifierExpression()
            {
                Value = "test"
            },
            Name = new SqlIdentifierExpression()
            {
                Value = "name"
            }
        },
        Operator = SqlBinaryOperator.EqualTo,
        Right = new SqlStringExpression()
        {
            Value = "a"
        }
    };
}

好了,现在我们添加完了,接下来我们肯定是想着把抽象语法树转化为sql语句,此时,就需要用上SqlGenerationAstVisitor了,他就是负责把抽象语法树转化为sql的

var sqlGenerationAstVisitor = new SqlGenerationAstVisitor(DbType.Oracle);
sqlAst.Accept(sqlGenerationAstVisitor);
var newSql = sqlGenerationAstVisitor.GetResult();

我们获取到的newSql就是新的sql了,他的值为

select * from test where(test.name = 'a')

至此,我们的目的就达到了。

7. sql解析的理论基础

sql之所以能被我们解析出来,主要是因为sql是一种形式语言,自然语言和形式语言的一个重要区别是,自然语言的一个语句,可能有多重含义,而形式语言的一个语句,只能有一个语义;形式语言的语法是人为规定的,有了一定的语法规则,语法解析器就能根据语法规则,解析出一个语句的唯一含义。

项目感悟

  1. 解决嵌套问题的唯一方案,就是用递归
  2. 对于基础项目,单元测试非常非常重要,因为开发的过程中可能会不断地重构,那以前跑过的测试案例就有可能失败,如果此时需要靠人手工去回归测试验证的话,那工作量是天量的,做不完,根本做不完,所以正确的解决方案是写单元测试,新添加一个功能后,为这个功能写1-N个单元测试,确保新功能对各种情况都有覆盖到,然后再跑一遍所有单元测试,确保没有影响到旧的功能。当然了,跑单元测试最让我崩溃的是,跑一遍所有单元测试,红了(即失败)几十个,天都塌了。

开源地址,欢迎star

本项目基于MIT协议开源,地址为
https://github.com/TripleView/SqlParser.Net

同时感谢以下项目

  1. 阿里巴巴开源的druid

写在最后

如果各位靓仔觉得这个项目不错,欢迎一键三连(推荐,star,关注),同时欢迎加入三合的开源交流群,QQ群号:799648362

Smarty实现页面静态化(生成HTML)的方法 / 张生荣

mikel阅读(59)

来源: Smarty实现页面静态化(生成HTML)的方法 / 张生荣

本文实例讲述了Smarty实现页面静态化(生成HTML)的方法。分享给大家供大家参考,具体如下:

为了减少数据库读取次数,某些内容不经常被更改的页面,比如文章详细页面需要做成HTML静态页面。

在使用Smarty的情况下,也可以实现页面静态化。下面先简单说一下使用Smarty时通常动态读取的做法。

一般分这几步:

1、通过URL传递一个参数(ID);

2、然后根据此ID查询数据库;

3、取得数据后根据需要修改显示内容;

4、assign需要显示的数据;

5、display模板文件。

Smarty静态化过程只需要在上述过程中添加两个步骤:

第一:在1之前使用 ob_start() 打开缓冲区。

第二:在5之后使用 ob_get_contents() 获取内存未输出内容,然后使用fwrite()将内容写入目标html文件。

根据上述描述,此过程是在网站前台实现的,而内容管理(添加、修改、删除)通常是在后台进行,为了能有效

利用上述过程,可以使用一点小手段,那就是Header()。具体过程是这样的:在添加、修改程序完成之后,使用

Header() (当然还有其它方式)跳到前台读取,这样可以实现页面HTML化,然后在生成html后再跳回后台管理侧,而这两个跳转

过程是不可见的。

  1. <?php
  2. $cachefile=“./cache/demo.html”;//把缓存文件放到一个cache文件夹里
  3. $cachetime=20;
  4. if (!file_exists($cachefile ) || filemtime($cachefile)+$cachetime < time()) //判断是否存在和过期时间
  5. {
  6. ob_start();//输出控制
  7. echo ‘<table border=”1″ width=”800″ align=”center”>’;
  8. echo ‘<caption><h1>user</h1></caption>’;
  9. echo ‘<tr>’;
  10. echo “<td>11111</td>”;
  11. echo “<td>22222</td>”;
  12. echo ‘</tr>’;
  13. echo ‘<tr>’;
  14. echo “<td>11111</td>”;
  15. echo “<td>22222</td>”;
  16. echo ‘</tr>’;
  17. echo ‘</table>’;
  18. $html=ob_get_contents();
  19. file_put_contents($cachefile, $html);//输出到缓存文件
  20. ob_end_flush();//输出并关闭缓冲区
  21. }
  22. else{
  23. echo ‘ceshi’;
  24. include $cachefile;
  25. }
  26. ?>

更多关于Smarty相关内容感兴趣的读者可查看本站专题:《smarty模板入门基础教程》、《PHP模板技术总结》、《PHP基于pdo操作数据库技巧总结》、《PHP运算与运算符用法总结》、《PHP网络编程技巧总结》、《PHP基本语法入门教程》、《php面向对象程序设计入门教程》、《php字符串(string)用法总结》、《php+mySQL数据库操作入门教程》及《php常见数据库操作技巧汇总》

希望本文所述对大家基于smarty模板的PHP程序设计有所帮助。

使用PHP Smarty静态化,使用smarty模板或缓存实现页面静态化-CSDN博客

mikel阅读(56)

来源: 使用PHP Smarty静态化,使用smarty模板或缓存实现页面静态化-CSDN博客

第一种:利用模板。目前PHP的模板可以说是很多了,有功能强大的smarty,还有简单易用的smarttemplate等。它们每一种模板,都有一个获取输出内容的函数。我们生成静态页面的方法,就是利用了这个函数。用这个方法的优点是,代码比较清晰,可读性好。

这里我用smarty做例子,说明如何生成静态页

require(‘smarty/Smarty.class.php’);

$t = new Smarty;

$t->assign(“title”,”Hello World!”);

$content = $t->fetch(“templates/index.htm”);

//这里的 fetch() 就是获取输出内容的函数,现在$content变量里面,就是要显示的内容了

$fp = fopen(“archives/2005/05/19/0001.html”, “w”);

fwrite($fp, $content);

fclose($fp);

第二种方法:利用ob系列的函数。这里用到的函数主要是 ob_start(), ob_end_flush(), ob_get_content(),其中ob_start()是打开浏览器缓冲区的意思,打开缓冲后,所有来自PHP程序的非文件头信息均不会发送,而是保存在内部缓冲区,直到你使用了ob_end_flush().而这里最重要的一个函数,就是ob_get_contents(),这个函数的作用是获取缓冲区的内容,相当于上面的那个fetch(),道理一样的。代码:

ob_start();

echo “Hello World!”;

$content = ob_get_contents();//取得php页面输出的全部内容

$fp = fopen(“archives/2005/05/19/0001.html”, “w”);

fwrite($fp, $content);

fclose($fp);

我选用的第2种方法 也就是用ob系列的函数

我刚开始看这个的时候有点不太明白 后来才知道ob是output buffering的意思 也就是输出缓存

当你准备输出的时候 所有的数据都保存在ob里面 服务器解析完php以后 把所有要输出到客户端的html代码都存放在ob里面 如果我们要输出html静态页面 只要把缓存取出来写入一个html页面即可

所以原理其实是很简单的

这里用到了几个函数 由于我初学php 很多函数我还不了解 所以这里也说明一下 希望可以帮助大家

ob_start():开始“捕捉”缓存 也就是从这里开始 打开浏览器的缓存

ob_end_flush():关闭浏览器缓存

ob_get_content():读取缓存内容

fopen(“文件路径”,”打开模式”)打开文件 这个函数的打开模式有好几种 下面介绍几种主要的模式:

‘r’ 只读方式打开,将文件指针指向文件头。??

‘r+’ 读写方式打开,将文件指针指向文件头。??

‘w’ 写入方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。??

‘w+’ 读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。??

fwrite(“文件名称”,”写入内容”) 写入文件

fclose() 关闭文件

由于我要转换的html文件非常多 可能有几百个 所以这里不能静态指定fopen的路径 大家可以设置一个路径变量 里面可以保存用户传来的id等信息 方便进行html文件命名 下面是我结合上次php读取xml数据的一个简单例子

程序代码

ob_start();????????????//打开浏览器缓存

//下面是读取xml数据

$parser = xml_parser_create(); //创建一个parser编辑器

xml_set_element_handler($parser, “startElement”, “endElement”);//设立标签触发时的相应函数 这里分别为startElement和endElenment

xml_set_character_data_handler($parser, “characterData”);//设立数据读取时的相应函数

$xml_file=”1.xml”;//指定所要读取的xml文件,可以是url

$filehandler = fopen($xml_file, “r”);//打开文件

while ($data = fread($filehandler, 4096))

{

xml_parse($parser, $data, feof($filehandler));

}//每次取出4096个字节进行处理

fclose($filehandler);

xml_parser_free($parser);//关闭和释放parser解析器

$name=false;

$position=false;

function startElement($parser_instance, $element_name, $attrs) //起始标签事件的函数

{

global $name,$position;??

if($element_name==”NAME”)

{

$name=true;

$position=false;

echo “名字:”;

}

if($element_name==”POSITION”)

{$name=false;

$position=true;

echo “职位:”;

}

}

function characterData($parser_instance, $xml_data) //读取数据时的函数

{

??global $name,$position;

??if($position)

echo $xml_data.”

“;

if($name)

echo $xml_data.”

“;

}

function endElement($parser_instance, $element_name) //结束标签事件的函数

{

global $name,$position;

$name=false;

$position=false;

}

?>

//xml数据读取完毕

$htmlname=$id.”.html”;??????????????????????????//$id可以自己定义 这里代表用户传来的id

$htmlpath=”archives/”.$htmlname;?????? //设置路径变量

$content = ob_get_contents();??????????????//取得php页面输出的全部内容

$fp = fopen($htmlpath, “w”);

fwrite($fp, $content);

fclose($fp);

?>

ThinkPHP 的页面静态化功能的实现_thinkphp 静态化-CSDN博客

mikel阅读(50)

来源: ThinkPHP 的页面静态化功能的实现_thinkphp 静态化-CSDN博客

**
常说的页面静态化分为两种,一种是伪静态,即url 重写,一种是真静态化。

上一篇讲到了ThinkPHP自带的buildHtml()方法生成静态页面,在ThinkPHP的官方文档也没具体讲到此方法,而文档却很具体的讲了静态缓存技术,看来TP官方还是建议使用静态缓存。

一. 网站不分手机版和电脑版,只有一套前台模板

(1)在根目录下的程序入口文件index.php中加下面这行:

define(‘HTML_PATH’, ‘./HTML/’);//生成静态页面的文件位置

(2)在项目的配置文件config.php中进行如下配置:
‘HTML_CACHE_ON’ => true, // 开启静态缓存
‘HTML_CACHE_TIME’ => 604800, // 全局静态缓存有效期(秒)(3600247)
‘HTML_FILE_SUFFIX’=> ‘.html’, // 设置静态缓存文件后缀
‘HTML_CACHE_RULES’=> array( // 定义静态缓存规则
// 定义格式1 数组方式
// 定义格式2 字符串方式
//后一个参数是静态缓存有效期,单位为秒。如果不定义,则会获取配置参数HTML_CACHE_TIME 的设置值,如果定义为0则表示永久缓存。
‘Index:index’ =>array(’{:controller}{:action}’),
‘Index:articalList’ =>array(’{:controller}
{:action}/{id}{p}list’),
‘Index:articalInfo’ =>array(’{:controller}{:action}/{id}’),
‘Index:productList’ =>array(’{:controller}
{:action}/{id}{p}list’),
‘Index:productInfo’ =>array(’{:controller}{:action}/{id}’),
//‘Index:staticcachec’ =>array(’{:module}/{:controller}/{:action}/{id}’,30),
//‘Index:staticcachec’ =>array(’{:module}/{:controller}/{:action}
{id}’,30),
)
{:module} 、{:controller} 和{:action} 分别表示当前模块名、控制器名和操作名,{id} 其实等效于 {$_GET.id},{p}则表示分页。在{}之外的字符作为字符串对待,如果包含有”/”,会自动创建目录。
在前台第一次访问某个模块的某个控制器的某个方法时,就会在指定的目录按照指定的文件名格式生成静态HTML文件。如图:

此时访问网站虽然URL没有变,但访问到的就是静态化之后的页面。亲测有效,但是此法也有不足:

如果系统分电脑版和手机版两套模板,那么生成的静态页面只有一套,手机先访问就生成手机的模板,再用电脑访问的界面就会混乱。

但如若网站只有一套模板(比如电脑版)那么到此就够了。如果手机电脑两套模板,解决方法如下

二. 网站分手机版和电脑版两套模板

(1)在根目录下的程序入口文件index.php中进行如下配置:

define(‘HTML_PATH’, ‘./HTML/’);//生成静态页面的文件位置

//如果是手机访问,设置手机缓存目录
if(isMobile()){
KaTeX parse error: Expected ‘EOF’, got ‘}’ at position 37: …] = ‘mobile/’; }̲elseif(isset(_SESSION[‘theme_path’])){
unset($_SESSION[‘theme_path’]);
}

//判断是否是手机访问
function isMobile(){
if(isset(KaTeX parse error: Double subscript at position 16: _SERVER[‘HTTP_X_̲WAP_PROFILE’]))…_SERVER[‘HTTP_VIA’]))return stristr(S E R V E R [ ′ H T T P V I A ′ ] , &quot; w a p &quot; ) ? t r u e : f a l s e ; i f ( i s s e t ( _SERVER[&#x27;HTTP_VIA&#x27;], &quot;wap&quot;) ? true : false; if(isset(SERVER[HTTPVIA],wap)?true:false;if(isset(_SERVER[‘HTTP_USER_AGENT’])){$clientkeywords = array (‘nokia’,‘sony’,‘ericsson’,‘mot’,‘samsung’,‘htc’,‘sgh’,‘lg’,‘sharp’,‘sie-’,‘philips’,‘panasonic’,‘alcatel’,‘lenovo’,‘iphone’,‘ipod’,‘blackberry’,‘meizu’,‘Android’,‘netfront’,‘symbian’,‘ucweb’,‘windowsce’,‘palm’,‘operamini’,‘operamobi’,‘openwave’,‘nexusone’,‘cldc’,‘midp’,‘wap’,‘mobile’);if (preg_match(“/(” . implode(’|’, c l i e n t k e y w o r d s ) . &quot; ) / i &quot; , s t r t o l o w e r ( clientkeywords) . &quot;)/i&quot;, strtolower(clientkeywords).)/i,strtolower(_SERVER[‘HTTP_USER_AGENT’])))return true;}
if(isset(KaTeX parse error: Expected ‘}’, got ‘EOF’ at end of input: …])){if((strpos(_SERVER[‘HTTP_ACCEPT’], ‘vnd.wap.wml’) !== false) && (strpos(S E R V E R [ ′ H T T P A C C E P T ′ ] , ′ t e x t / h t m l ′ ) = = = f a l s e ∣ ∣ ( s t r p o s ( _SERVER[&#x27;HTTP_ACCEPT&#x27;], &#x27;text/html&#x27;) === false || (strpos(SERVER[HTTPACCEPT],text/html)===false(strpos(_SERVER[‘HTTP_ACCEPT’], ‘vnd.wap.wml’) < strpos($_SERVER[‘HTTP_ACCEPT’], ‘text/html’))))return true;}
return false;
}

(2)在项目的配置文件config.php中进行如下配置:
//设置静态缓存
‘HTML_CACHE_ON’ => true, // 开启静态缓存
‘HTML_CACHE_TIME’ => 604800, // 全局静态缓存有效期(秒)(3600247)
‘HTML_FILE_SUFFIX’=> ‘.html’, // 设置静态缓存文件后缀
‘HTML_CACHE_RULES’=> array( // 定义静态缓存规则
// 定义格式2 字符串方式
‘Index:index’ =>array(’{KaTeX parse error: Expected ‘EOF’, got ‘}’ at position 20: …SION.theme_path}̲/{:controller}_…SESSION.theme_path}/{:controller}{:action}/{id}_{p}list’),
‘Index:articalInfo’ =>array(’{KaTeX parse error: Expected ‘EOF’, got ‘}’ at position 20: …SION.theme_path}̲/{:controller}_…SESSION.theme_path}/{:controller}{:action}/{id}
{p}_list’),
//‘Index:productInfo’ =>array(’{$SESSION.theme_path}/{:controller}{:action}/{id}’),
)

那么手机访问的时候,会先以缓存的方式生成一个名字为theme_path内容为mobile/的字符串缓存,然后在HTML/mobile/目录下生成相应的手机模板静态文件;如果再用电脑访问,那么会先清除名字为theme_path的字符串缓存内容,那么再生成静态文件的时候就在HTML/目录下了。

作者:江南极客
来源:CSDN
原文:https://blog.csdn.net/sinat_35861727/article/details/54971805
版权声明:本文为博主原创文章,转载请附上博文链接!**

TinkPHP6-tp6实现全站静态化方法-ThinkPHP-萝卜网络博客

mikel阅读(53)

来源: TinkPHP6-tp6实现全站静态化方法-ThinkPHP-萝卜网络博客

一,前言

模板完全静态化,也就是通过模板完全生成纯静态的网页,相比动态页面和伪静态页面更安全更利于SEO访问更快。

二,实现思路

1,根据模块/控制器_MD5(参数)动态递归创建目录

2,file_exists判断生成的静态页是否存在,是否过期,存在并且未过期则重定向到静态网页

3,不存在或者文件已过期,则file_put_contents($file,$content)函数生成静态页面

三,编码

1,基类中的生成前与生成后的方法

class Common extends BaseController
{
    //静态模板生成目录
    protected $staticHtmlDir = "";
    //静态文件
    protected $staticHtmlFile = "";             

    //判断是否存在静态
    public function beforeBuild($param = []) {
        //生成静态
        $this->staticHtmlDir = "html".DS.$this->request->controller().DS;
        //参数md5
        $param = md5(json_encode($param));
        $this->staticHtmlFile = $this->staticHtmlDir .$this->request->action() . '_'  . $param .'.html';
        
        //目录不存在,则创建
        if(!file_exists($this->staticHtmlDir)){
            mkdir($this->staticHtmlDir);
        }

        //静态文件存在,并且没有过期
        if(file_exists($this->staticHtmlFile) && filectime($this->staticHtmlFile)>=time()-60*60*24*5) {
            header("Location:/" . $this->staticHtmlFile);
            exit();
        }

    }

    //开始生成静态文件
    public function afterBuild($html) {
        if(!empty($this->staticHtmlFile) && !empty($html)) {
            if(file_exists($this->staticHtmlFile)) {
                \unlink($this->staticHtmlFile);
            }
            if(file_put_contents($this->staticHtmlFile,$html)) {
                header("Location:/" . $this->staticHtmlFile);
                exit();
            }
        }
    }

}

2,控制器中的使用

public function getList($id = '')
{
    //判断静态界面是否存在
    $this->beforeBuild(array($id));

    // do someing
    $name = "测试静态化";
    $html = View::fetch('/get_list',['name'=>$name]);

    //生成静态界面
    $this->afterBuild($html);

}

一套以用户体验出发的.NET8 Web开源框架 - 追逐时光者 - 博客园

mikel阅读(60)

来源: 一套以用户体验出发的.NET8 Web开源框架 – 追逐时光者 – 博客园

前言

今天大姚给大家分享一套以用户体验出发的.NET8 Web开源框架:YiFramework。

项目介绍

YiFramework是一个基于.NET8 + Abp.vNext + SQLSugar 的DDD领域驱动设计后端开源框架,前端使用Vue3,项目架构模式三层架构\DDD领域驱动设计,内置RBAC权限管理、BBS论坛社区系统 以用户体验出发。架构干净整洁、采用微软风格原生框架封装。适合小中大型项目上线、.NET8学习、Abp.vNext学习、SQLsugar学习 、项目二次开发。

项目特点

框架简单易用,框架不以打包形式引用,而是直接以项目附带源码给出,自由度拉满,遵循MIT License协议,允许随意修改(请注明来源即可)。

核心技术

后端

.NET8、Abp.vNext、Jwt、Serilog、Autofac、Mapster、SQLsugarCore、Quartz.Net等。

前端

vue3、axios、echarts、element-plus、vue-router、vite、pinia等。

运维

nginx、gitlab、Jenkins、harbor。

业务支持模块

RABC权限管理系统

用户管理、角色管理、菜单管理、部门管理、岗位管理、字典管理、参数管理、用户在线、登录日志、定时任务、服务监控等功能模块。

BBS社区论坛系统

文章功能、板块功能、主题功能、个人中心、授权中心、权限管理。

演示截图

 

项目源码地址

更多项目实用功能和特性欢迎前往项目开源地址查看👀,别忘了给项目一个Star支持💖。

优秀项目和框架精选

该项目已收录到C#/.NET/.NET Core优秀项目和框架精选中,关注优秀项目和框架精选能让你及时了解C#、.NET和.NET Core领域的最新动态和最佳实践,提高开发工作效率和质量。坑已挖,欢迎大家踊跃提交PR推荐或自荐(让优秀的项目和框架不被埋没🤞)。

大模型应用开发初探 : 手搓一个简易Agent - EdisonZhou - 博客园

mikel阅读(89)

来源: 大模型应用开发初探 : 手搓一个简易Agent – EdisonZhou – 博客园

大家好,我是Edison。

今天是中秋节前最后一个工作日,加油挺住,马上就放假了!

近期我一直在学习和了解LLM的相关知识,听到大家都在谈论AI Agent,说它是接下来几年大模型应用开发的新范式,那么什么是AI Agent,如何快速开发一个AI Agent呢?

AI Agent:可以帮你执行任务的助手

学术界和工业界对术语“AI Agent”提出了各种定义。其中,OpenAI将AI Agent定义为“以大语言模型为大脑驱动的系统,具备自主理解、感知、规划、记忆和使用工具的能力,能够自动化执行完成复杂任务的系统。”

说人话就是:大多数时候你给它一个最终你想要达成的目标,它能直接交付结果,过程你啥都不用管

NOTE:如果说人和动物的区别是人会使用各种工具,那么Agent和大模型的区别亦然。

我们可以把Agent与LLM形象地比作生物体与其大脑,Agent有手有脚,可以自己干活自己执行,而LLM呢,就是它的大脑。比如,如果你使用LLM大模型,它可能只能给你输出一份食谱,告诉你需要哪些食材和步骤来制作。但如果你使用Agent,它可能就是不仅提供食谱和步骤,还会根据你的需求,帮你选择合适的食材甚至自动下单购买,监控烹饪过程,确保食物口感,最终为你呈上一份佳肴。

AI Agent如何工作?

AI Agent的架构是其智能行为的基础,它通常包括感知、规划、记忆、工具使用和行动等关键组件,这些组件协同工作以实现高效的智能行为。

AI Agent的工作流程其实就是一个连续的循环过程

它从感知环境开始,经过信息处理、规划和决策,然后执行行动。最后,根据执行结果和环境反馈进行调整,以优化未来的行动和决策。

通过这种结构化和层次化的方式,AI Agent能够有效地处理信息,做出决策,并在复杂环境中执行任务。

如何开发AI Agent?

目前业界开发AI Agent主要有两种模式:

一种是基于Python或C#等编程语言,结合LangChain或Semantic Kernel等大模型应用开发框架,集成某个大模型API 和 企业内部的业务API能力,来完成具体领域的Agent。

另一种是基于Coze、Dify、AutoGen等Agent开发管理平台,拖过拖拉拽的方式快速生成一个Agent,与其说是开发,不如说是Workflow一样的配置。当然,也需要给这些平台注册封装好的企业内部API平台提供的能力供配置好的Agent去实现工具调用。

使用Semantic Kernel开发AI Agent

这里我们快速使用Semantic Kernel开发一个简易的WorkOrder Agent(MES工单助手),重点关注如何给LLM添加Function Calling能力,直观了解Agent规划任务 和 执行任务 的效果,而至于其他更加具体的,等待后续了解深入后再交流,这里我们就先来个感性认识即可。

以终为始,先看效果吧:

(1)没有实现Function Calling的效果,它就只是个Chatbot

(2)实现了Function Calling的效果,它就可以称为Agent

可以看到,我的需求其实包含两个步骤:第一步是更新工单的Quantity,第二步是查询更新后的工单信息。而这两个步骤我们假设其实都是需要去调用MES WorkOrderService API才能获得的,而这就需要我们给LLM加入Function Calling的能力,当然LLM自己得知道如何规划执行的步骤,哪个步骤先执行,哪个后执行。

示例代码的结构如下所示:

关键部分代码:

(1)Shared

OpenAiConfiguration.cs

复制代码
public class OpenAiConfiguration
{
    public string Provider { get; set; }
    public string ModelId { get; set; }
    public string EndPoint { get; set; }
    public string ApiKey { get; set; }

    public OpenAiConfiguration(string modelId, string endPoint, string apiKey)
    {
        Provider = ConfigConstants.LLMProviders.OpenAI; // Default OpenAI-Compatible LLM API Provider
        ModelId = modelId;
        EndPoint = endPoint;
        ApiKey = apiKey;
    }

    public OpenAiConfiguration(string provider, string modelId, string endPoint, string apiKey)
    {
        Provider = provider;
        ModelId = modelId;
        EndPoint = endPoint;
        ApiKey = apiKey;
    }
}
复制代码

CustomLLMApiHandler.cs

复制代码
public class CustomLLMApiHandler : HttpClientHandler
{
    private readonly string _openAiProvider;
    private readonly string _openAiBaseAddress;

    public CustomLLMApiHandler(string openAiProvider, string openAiBaseAddress)
    {
        _openAiProvider = openAiProvider;
        _openAiBaseAddress = openAiBaseAddress;
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        UriBuilder uriBuilder;
        Uri uri = new Uri(_openAiBaseAddress);
        switch (request.RequestUri?.LocalPath)
        {
            case "/v1/chat/completions":
                switch (_openAiProvider)
                {
                    case ConfigConstants.LLMProviders.ZhiPuAI:
                        uriBuilder = new UriBuilder(request.RequestUri)
                        {
                            Scheme = "https",
                            Host = uri.Host,
                            Path = ConfigConstants.LLMApiPaths.ZhiPuAIChatCompletions,
                        };
                        request.RequestUri = uriBuilder.Uri;
                        break;
                    default: // Default: OpenAI-Compatible API Providers
                        uriBuilder = new UriBuilder(request.RequestUri)
                        {
                            Scheme = "https",
                            Host = uri.Host,
                            Path = ConfigConstants.LLMApiPaths.OpenAIChatCompletions,
                        };
                        request.RequestUri = uriBuilder.Uri;
                        break;
                }
                break;
        }

        HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
        return response;
    }
}
复制代码

(2)WorkOrderService

这里我直接模拟的API的逻辑,你可以使用HttpClient去实际调用某个API。

复制代码
public class WorkOrderService
{
    private static List<WorkOrder> workOrders = new List<WorkOrder>
            {
                new WorkOrder { WorkOrderName = "9050100", ProductName = "A5E900100", ProductVersion = "001 / AB", Quantity = 100, Status = "Ready" },
                new WorkOrder { WorkOrderName = "9050101", ProductName = "A5E900101", ProductVersion = "001 / AB", Quantity = 200, Status = "Ready" },
                new WorkOrder { WorkOrderName = "9050102", ProductName = "A5E900102", ProductVersion = "001 / AB", Quantity = 300, Status = "InProcess" },
                new WorkOrder { WorkOrderName = "9050103", ProductName = "A5E900103", ProductVersion = "001 / AB", Quantity = 400, Status = "InProcess" },
                new WorkOrder { WorkOrderName = "9050104", ProductName = "A5E900104", ProductVersion = "001 / AB", Quantity = 500, Status = "Completed" }
            };

    public WorkOrder GetWorkOrderInfo(string orderName)
    {
        return workOrders.Find(o => o.WorkOrderName == orderName);
    }

    public string UpdateWorkOrderStatus(string orderName, string newStatus)
    {
        var workOrder = this.GetWorkOrderInfo(orderName);
        if (workOrder == null)
            return "Operate Failed : The work order is not existing!";
        // Update status if it is valid
        workOrder.Status = newStatus;
        return "Operate Succeed!";
    }

    public string ReduceWorkOrderQuantity(string orderName, int newQuantity)
    {
        var workOrder = this.GetWorkOrderInfo(orderName);
        if (workOrder == null)
            return "Operate Failed : The work order is not existing!";

        // Some business checking logic like this
        if (workOrder.Status == "Completed")
            return "Operate Failed : The work order is completed, can not be reduced!";
        if (newQuantity <= 1 || newQuantity >= workOrder.Quantity)
            return "Operate Failed : The new quantity is invalid!";
        // Update quantity if it is valid
        workOrder.Quantity = newQuantity;
        return "Operate Succeed!";
    }
}
复制代码

(3)Form

appsetting.json

复制代码
{
  "LLM_API_PROVIDER": "ZhiPuAI",
  "LLM_API_MODEL": "glm-4",
  "LLM_API_BASE_URL": "https://open.bigmodel.cn",
  "LLM_API_KEY": "***********" // Update this value to yours
}
复制代码

AgentForm.cs

初始化Kernel

复制代码
 private Kernel _kernel = null;
 private OpenAIPromptExecutionSettings _settings = null;
 private IChatCompletionService _chatCompletion = null;
 private ChatHistory _chatHistory = null;

private void ChatForm_Load(object sender, EventArgs e)
    {
        var configuration = new ConfigurationBuilder().AddJsonFile($"appsettings.ZhiPu.json");
        var config = configuration.Build();
        var openAiConfiguration = new OpenAiConfiguration(
            config.GetSection("LLM_API_PROVIDER").Value,
            config.GetSection("LLM_API_MODEL").Value,
            config.GetSection("LLM_API_BASE_URL").Value,
            config.GetSection("LLM_API_KEY").Value);
        var openAiClient = new HttpClient(new CustomLlmApiHandler(openAiConfiguration.Provider, openAiConfiguration.EndPoint));
        _kernel = Kernel.CreateBuilder()
            .AddOpenAIChatCompletion(openAiConfiguration.ModelId, openAiConfiguration.ApiKey, httpClient: openAiClient)
            .Build();

        _chatCompletion = _kernel.GetRequiredService<IChatCompletionService>();

        _chatHistory = new ChatHistory();
        _chatHistory.AddSystemMessage("You are one WorkOrder Assistant.");
    }
复制代码

注册Functions

复制代码
_kernel.Plugins.Add(KernelPluginFactory.CreateFromFunctions("WorkOrderHelperPlugin",
    new List<KernelFunction>
    {
                    _kernel.CreateFunctionFromMethod((string orderName) =>
                    {
                        var workOrderRepository = new WorkOrderService();
                        return workOrderRepository.GetWorkOrderInfo(orderName);
                    }, "GetWorkOrderInfo", "Get WorkOrder's Detail Information"),
                    _kernel.CreateFunctionFromMethod((string orderName, int newQuantity) =>
                    {
                        var workOrderRepository = new WorkOrderService();
                        return workOrderRepository.ReduceWorkOrderQuantity(orderName, newQuantity);
                    }, "ReduceWorkOrderQuantity", "Reduce WorkOrder's Quantity to new Quantity"),
                    _kernel.CreateFunctionFromMethod((string orderName, string newStatus) =>
                    {
                        var workOrderRepository = new WorkOrderService();
                        return workOrderRepository.UpdateWorkOrderStatus(orderName, newStatus);
                    }, "UpdateWorkOrderStatus", "Update WorkOrder's Status to new Status")
     }
));
复制代码

开启自动调用Function,告诉大模型可以自行决定调用相关Functions,而且大模型会自行决定根据什么顺序来调用,这就是大模型作为Agent大脑的规划能力。当然,我们还可以通过定制化Planner来增强agent的规划能力,这个就留到后面再分享:

_settings = new OpenAIPromptExecutionSettings
{
   ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};

发送用户的提示词给大模型:

复制代码
_chatHistory.AddUserMessage(tbxPrompt.Text);
        ChatMessageContent chatResponse = null;
        tbxResponse.Clear();

if (cbxUseFunctionCalling.Checked)
{
            Task.Run(() =>
            {
                ShowProcessMessage("AI is handling your request now...");
                chatResponse = _chatCompletion.GetChatMessageContentAsync(_chatHistory, _settings, _kernel)
                    .GetAwaiter()
                    .GetResult();
                UpdateResponseContent(chatResponse.ToString());
                ShowProcessMessage("AI Response:");
            });
        }
else
{
            Task.Run(() =>
            {
                ShowProcessMessage("AI is handling your request now...");
                chatResponse = _chatCompletion.GetChatMessageContentAsync(_chatHistory, null, _kernel)
                    .GetAwaiter()
                    .GetResult();
                UpdateResponseContent(chatResponse.ToString());
                ShowProcessMessage("AI Response:");
            });
}
复制代码

小结

本文简单介绍了AI Agent的基本概念 和 工作方式,目前主要有两种开发Agent的模式,一种是高代码手搓,另一种是低代码拖拉拽。

最后,本文通过C# + Semantic Kernel + 智谱GLM-4模型 演示了如何快速开发一个简易的AI Agent,虽然它只是个Demo,但希望对你快速了解Agent有所帮助!

示例源码

本文示例:https://github.com/Coder-EdisonZhou/EDT.Agent.Demos

本文大模型:智谱 GLM-4 模型

推荐学习

Microsoft Learn, 《Semantic Kernel 学习之路

大模型应用开发初探 : 基于Coze创建Agent - EdisonZhou - 博客园

mikel阅读(117)

来源: 大模型应用开发初探 : 基于Coze创建Agent – EdisonZhou – 博客园

大家好,我是Edison。

最近学习了一门课程《AI Agent入门实战》,了解了如何在Coze平台上创建AI Agent,发现它对我们个人(C端用户)而言十分有用,分享给你一下。

Coze是什么?

Coze(扣子)是字节跳动公司开发的新一代AI应用开发平台,使用这个AI应用开发平台,无论你是否有编码基础,都可以快速搭建基于大语言模型的各类AI Bot,还可以将Bot发布到其他渠道。对于一个AI Agent而言,最重要的能力就是任务规划、调用工具、知识库 和 记忆能力,而这些能力在Coze中你都不需要关注,已经封装好了提供给你,对你而言就是透明的。

如上图所示,我们可以对我们要做的AI Agent先设立人设,然后给它注册想要调用的工具或工作流,还可以给它注册一个内部知识库(文档/图片/表格等),如果想要记忆能力甚至可以直接给它添加一个数据库供其使用,最后再通过调试模块进行测试,一个针对AI Agent的“宇宙最强IDE“也不过如此。
目前,Coze有两个版本:

(1)基础版:面向尝鲜体验的个人和企业开发者,全部功能免费使用,但有一定的限量额度,超过后不可再使用,需切换专业版后继续使用。

(2)专业版:面向对稳定性和用量有更高需求的专业开发者,支持更高团队空间容量和免费知识库容量,付费功能保障专业级 SLA,不限制调用请求频率和总量,费用按实际用量计算。

这里,我用的是基础版,主要是尝尝鲜,做了几个DEMO体验下效果,用到的模型主要是豆包的Function Call模型。未来,我们可能会主要尝试企业内部搭建的FastGPT或Dify,又或者是微软的AutoGen。

下面,主要通过我做的这几个DEMO一起来看看Coze提供的一些关键能力。

强大的工作流配置

我通过Coze创建了一个城市天气助手的Bot,使用了Coze提供的工作流能力,如下图所示,这是一个获取天气预报并解析的工作流:

可以看到,通过一个简单的工作流,我们就快速调用了大模型 和 插件(墨迹天气)的能力,而这些操作在传统的编码场景下,都需要程序员单独来处理,现在则是0代码纯配置就可以了。

基于这个工作流,我再把人设和回复的逻辑配置一下提示词,就可以完成一个Bot的创建。值得一提的是,针对你的提示词,Coze提供了一个优化的功能,可以按照最佳实践将你的提示词做一个优化,这真的是一个很实用的功能。

最后实现的效果如下图所示:

强大的图像流配置

我通过Coze创建了一个产品图背景替换助手的Bot,用到了Coze提供的另一个强大技能:图像流。这也是一个工作流,但是其用到了专门针对图像处理的处理节点,例如图像生成、背景替换、画质提升等等。这些功能对于有做社交媒体运营的朋友,应该挺有帮助的。

最后的效果如下图所示:我把原图 和 想要替换的背景描述给它,它给我输出了一张还算不错的背景替换图。

快捷的知识库应用

Coze支持不同格式的知识库,例如文本类型(如txt, pdf, doc等)、表格类型(如xls等)以及 照片类型(如png, jpg等)。

比如,我创建了一个MongoDB知识助手的Bot,就导入了一些MongoDB的体系课程的pdf文档:

最终的效果如下图所示:

又如,我创建了一个产品图查询助手的Bot,可以基于我导入的产品图资料库,让我可以快速的查找到对应的产品图。

效果如下图所示:

再如,假设我是一个在线课堂的老板,我将课程订单表(Excel)导入到知识库中,通过对人设和回复逻辑的设置,就可以实现一个快速查询的功能:

透明的记忆能力

假设我是一个在线课堂的老板,我可以用Coze创建一个在线客服,让它和客户对话,并试图引导用户留下姓名和联系方式,这就需要一个类似于数据库的记忆能力。

这样配置后,一旦客户在对话中留下联系方式,我们的Bot就会自动将其存入预先设置的数据库中:

其他能力

对于客服类Bot,语音能力是非常重要的,在Coze中可以支持语音通话,还有多种口音供选择,个人觉得这是很方便的一个支持能力。

发布到订阅号

Coze可以支持发布到多个平台,未来可能真的会有Agent Store的概念。不过,我目前最喜欢的还是可以直接发布到微信订阅号,这样大家在给我回复时,不只是有冷冰冰的自动回复,而是有情绪价值的回复,for all of you!

小结

本文简单介绍了Coze(扣子)这个AI应用开发平台的主要功能,通过我所学习实践的一些DEMO来了解一下在AI Agent开发中涉及到一些核心概念如工作流、图像流、记忆能力、知识库等等,相信会对大家在今后的AI Agent开发实践中有所帮助。

推荐学习

周文洋,《AI Agent入门实战》

Log Parser 2.2 + Log Parser Lizard GUI 分析IIS日志示例_log parser lizard gui:-CSDN博客

mikel阅读(88)

来源: Log Parser 2.2 + Log Parser Lizard GUI 分析IIS日志示例_log parser lizard gui:-CSDN博客

Log Parser 日志分析工具,用命令行操作,可以分析 IIS logs,event logs,active directory,log4net,file system,t-SQL

Log Parser Lizard 以可视化界面操作,使用类似SQL的语法查询

 

下载地址:

Log Parser 2.2 :
http://www.microsoft.com/en-us/download/details.aspx?id=24659

Log Parser Lizard GUI :
http://www.lizard-labs.com/log_parser_lizard_buy.aspx

 

或者两个打包好的资源下载:

LogParser2.2+LogParserLizard

 

安装完成后:

 

 

 

LogParser 查询方法:

 

编写SQL脚本保存为: SlowPage.sql

–C:\inetpub\SlowPage.sql
Select Top 20
LogRow as [Line Number],
date as [Date],
time as [Time],
c-ip as [Client-IP],
s-ip as [Server IP],
s-port as [Server Port],
cs-method as [Request Verb],
cs-uri-stem as [Request URI],
sc-bytes as [Bytes sent],
sc-status as [Status],
sc-substatus as [Sub-status],
sc-win32-status as [Win 32 Status],
time-taken as [Time Taken]
From
C:\inetpub\logs\LogFiles\W3SVC2\u_ex*.log
Order by time-taken desc

打开 LogParser 2.2 命令行工具,执行脚本:
LOGPARSER -i:IISW3C file:C:\inetpub\SlowPage.sql -o:DataGrid -q:off

 

 

Log Parser Lizard 查询方法:

打开 Log Parser Lizard 可视化界面,如下图,将sql命令复制到 查询[query] 窗口中,在点击生成[generate]。

 

 

 

 

还可以以各种图形的方式显示出来,更方便、直观 了解分析情况:

 

 

 

在Log Parser 2.2 安装路径下,有很多自带的sql语句:

 

 

若不清楚列名称,可以先全部查询所有列,再选择有用的列进行分析:

 

 

更多的使用方法,可以参考帮助文档,有很多例子:

 

————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/kk185800961/article/details/43964209