斯坦福“草泥马”火了:100美元就能比肩GPT-3.5,手机都能运行的那种

mikel阅读(490)

来源: 斯坦福“草泥马”火了:100美元就能比肩GPT-3.5,手机都能运行的那种

一夜之间,大模型界又炸出个big news!

斯坦福发布Alpaca(羊驼,网友口中的“草泥马”):

只花100美元,人人都可微调Meta家70亿参数的LLaMA大模型,效果竟可比肩1750亿参数的GPT-3.5(text-davinci-003)。

而且还是单卡就能运行的那种,甚至树莓派、手机都能hold住!

还有一个更绝的“骚操作”。

研究所涉及到的数据集,是斯坦福团队花了不到500美元用OpenAI的API来生成的。

所以整个过程下来,就等同于GPT-3.5自己教出了个旗鼓相当的对手AI。

(薅羊毛高手……)

然后团队还说,用大多数云计算平台去微调训练好的模型,成本也不到100美元:

复制一个GPT-3.5效果的AI,很便宜,很容易,还很小。

而且团队还把数据集(秒省500刀)、代码统统都给开源了,这下子人人都能去微调个效果炸裂的对话AI:

项目在GitHub发布才半天时间,便已经狂揽1800+星,火爆程度可见一斑。

Django联合开发者甚至对斯坦福的新研究用“惊天大事”来形容:

不仅如此,斯坦福团队还搞了个demo,在线可玩的那种。

话不多说,我们现在就来看看这个“草泥马”的效果。

比肩davinci-003的草泥马Aplaca

在斯坦福官方的演示中,他们先小试牛刀地提了一个问题:

什么是羊驼?它和美洲驼的区别是什么?

草泥马Aplaca给出的答案较为干练:

羊驼是一种小型骆驼科动物,原产于秘鲁、玻利维亚、厄瓜多尔和智利;它比美洲驼小,羊毛更细,也没有驼峰。

而后又简单的介绍了二者群居生活的不同。

同样的问题若是交给ChatGPT(GPT3.5-turbo),则答案就不会像草泥马Aplaca那般简洁:

对此,团队给出的解释是:

Alpaca的答案通常比ChatGPT短,反映出text-davinci-003的输出较短。

而后团队演示了让草泥马Alpaca写邮件:

写一封e-mail祝贺被斯坦福大学录取的新生,并提到你很高兴能亲自见到他们。

草泥马Alpaca对于这个任务也是信手拈来,直接给出了一个像模像样的邮件模板:

难度再次进阶,团队这次提出了让草泥马Alpaca写论文摘要的需求:

写一篇经过深思熟虑的机器学习论文摘要,证明42是训练神经网络的最优seed。

草泥马Alpaca给出的答案从内容上来看,非常符合大多数论文的摘要形式:试图回答什么问题、用了什么方法、结果如何,以及未来展望。

当然,也有迫不及待的网友亲自下场试验,发现草泥马Alpaca写代码也是不在话下。

不过即便草泥马Alpaca能够hold住大部分问题,但这并不意味着它没有缺陷。

例如团队便演示了一个例子,在回答“坦桑尼亚的首都是哪里”的问题时,草泥马Alpaca给出的答案是“达累斯萨拉姆”。

但实际上早在1975年便被“多多马”取代了。

除此之外,若是亲自体验过草泥马Alpaca就会发现,它……巨慢:

对此,有网友认为可能是使用的人太多的原因。

笔记本、手机、树莓派都能跑

Meta开源的LLaMA大模型,刚发布几周就被大家安排明白了,单卡就能运行。

所以理论上,基于LLaMA微调的Alpaca同样可以轻松在本地部署。

没有显卡也没关系,苹果笔记本甚至树莓派、手机都可以玩。

在苹果笔记本部署LLaMA的方法来自GitHub项目llama.cpp,使用纯C/C++做推理,还专门对ARM芯片做了优化。

作者实测,M1芯片的MacBook Pro上即可运行,另外也支持Windows和Linux系统。

还是这个C++移植版本,有人成功在4GB内存的树莓派4上成功运行了LLaMA的70亿参数版本。

虽然速度非常慢,大约10秒生成一个token(也就是一分钟蹦出4.5个单词)。

更离谱的是仅仅2天之后,有人把LLaMA模型量化压缩(权重转换成更低精度的数据格式)后成功在Pixel6安卓手机上运行(26秒一个token)。

Pixel6使用谷歌自研处理器Google Tensor,跑分成绩在骁龙865+到888之间,也就是说新一点的手机理论上都能胜任。

微调数据集也开源

斯坦福团队微调LLaMA的方法,来自华盛顿大学Yizhong Wang等去年底提出的Self-Instruct。

以175个问题作为种子任务,让AI自己从中组合出新的问题以及生成配套答案实例,人工过滤掉低质量的,再把新任务添加到任务池里。

所有这些任务,之后可以采用InstructGPT的方法让AI学会如何遵循人类指令。

套娃几圈下来,相当于让AI自己指导自己。

斯坦福版Alpaca,就是花了不到500美元使用OpenAI API生成了5.2万个这样的示例搞出来的。

这些数据同样开源了出来,并且比原论文的数据多样性更高。

同时还给出了生成这些数据的代码,也就是说如果有人还嫌不够,可以再去自行扩充微调数据,继续提高模型的表现。

微调代码也会在HuggingFace官方支持LLaMA后放出。

不过Alpaca最终的模型权重需要Meta许可才能发布,并且继承了LLaMA的非商用开源协议,禁止任何商业用途。

并且由于微调数据使用了OpenAI的API,根据使用条款也禁止用来开发与OpenAI形成竞争的模型。

One More Thing

还记得AI绘画的发展历程吗?

2022年上半年还只是话题热度高,8月份Stable Diffusion的开源让成本下降到可用,并由此产生爆炸式的工具创新,让AI绘画真正进入各类工作流程。

语言模型的成本,如今也下降到了个人电子设备可用的程度。

最后还是由Django框架创始人Simon Willison喊出:

大语言模型的Stable Diffusion时刻到了。

ASP.NET Core Web API 接口限流 - 0611163 - 博客园

mikel阅读(422)

来源: ASP.NET Core Web API 接口限流 – 0611163 – 博客园

一. 前言

ASP.NET Core Web API 接口限流、限制接口并发数量,我也不知道自己写的有没有问题,抛砖引玉。

二. 需求

  1. 写了一个接口,参数可以传多个人员,也可以传单个人员,时间范围限制最长一个月。简单来说,当传单个人员时,接口耗时很短,当传多个人员时,一般人员会较多,接口耗时较长,一般耗时几秒。
  2. 当传多个人员时,并发量高时,接口的耗时就很长了,比如100个用户并发请求,耗时可长达几十秒,甚至1分钟。
  3. 所以需求是,当传单个人员时,不限制。当传多个人员时,限制并发数量。如果并发用户数少于限制数,那么所有用户都能成功。如果并发用户数,超出限制数,那么超出的用户请求失败,并提示”当前进行XXX查询的用户太多,请稍后再试”。
  4. 这样也可以减轻被请求的ES集群的压力。

三. 说明

  1. 使用的是.NET6
  2. 我知道有人写好了RateLimit中间件,但我暂时还没有学会怎么使用,能否满足我的需求,所以先自己实现一下。

四. 效果截图

下面是使用jMeter并发测试时,打的接口日志:

五. 代码

1. RateLimitInterface

接口参数的实体类要继承该接口

using JsonA = Newtonsoft.Json;
using JsonB = System.Text.Json.Serialization;

namespace Utils
{
    /// <summary>
    /// 限速接口
    /// </summary>
    public interface RateLimitInterface
    {
        /// <summary>
        /// 是否限速
        /// </summary>
        [JsonA.JsonIgnore]
        [JsonB.JsonIgnore]
        bool IsLimit { get; }
    }
}

2. 接口参数实体类

继承RateLimitInterface接口,并实现IsLimit属性

public class XxxPostData : RateLimitInterface
{
    ...省略

    /// <summary>
    /// 是否限速
    /// </summary>
    [JsonA.JsonIgnore]
    [JsonB.JsonIgnore]
    public bool IsLimit
    {
        get
        {
            if (peoples.Count > 2) //限速条件,自己定义
            {
                return true;
            }
            return false;
        }
    }
}

3. RateLimitAttribute

作用:标签打在接口方法上,并设置并发数量

namespace Utils
{
    /// <summary>
    /// 接口限速
    /// </summary>
    public class RateLimitAttribute : Attribute
    {
        private Semaphore _sem;

        public Semaphore Sem
        {
            get
            {
                return _sem;
            }
        }

        public RateLimitAttribute(int limitCount = 1)
        {
            _sem = new Semaphore(limitCount, limitCount);
        }
    }
}

4. 使用RateLimitAttribute

标签打在接口方法上,并设置并发数量。
服务器好像是24核的,并发限制为8应该没问题。

[HttpPost]
[Route("[action]")]
[RateLimit(8)]
public async Task<List<XxxInfo>> Query([FromBody] XxxPostData data)
{
    ...省略
}

5. 限制接口并发量的拦截器RateLimitFilter

/// <summary>
/// 接口限速
/// </summary>
public class RateLimitFilter : ActionFilterAttribute
{
    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        Type controllerType = context.Controller.GetType();
        object arg = context.ActionArguments.Values.ToList()[0];
        var rateLimit = context.ActionDescriptor.EndpointMetadata.OfType<RateLimitAttribute>().FirstOrDefault();

        bool isLimit = false; //是否限速
        if (rateLimit != null && arg is RateLimitInterface) //接口方法打了RateLimitAttribute标签并且参数实体类实现了RateLimitInterface接口时才限速,否则不限速
        {
            RateLimitInterface model = arg as RateLimitInterface;
            if (model.IsLimit) //满足限速条件
            {
                isLimit = true;
                Semaphore sem = rateLimit.Sem;

                if (sem.WaitOne(0)) //注意:超时时间为0,表示不等待
                {
                    try
                    {
                        await next.Invoke();
                    }
                    catch
                    {
                        throw;
                    }
                    finally
                    {
                        sem.Release();
                    }
                }
                else
                {
                    var routeList = context.RouteData.Values.Values.ToList();
                    routeList.Reverse();
                    var route = string.Join('/', routeList.ConvertAll(a => a.ToString()));
                    var msg = $"当前访问{route}接口的用户数太多,请稍后再试";
                    LogUtil.Info(msg);
                    context.Result = new ObjectResult(new ApiResult
                    {
                        code = (int)HttpStatusCode.ServiceUnavailable,
                        message = "当前查询的用户太多,请稍后再试。"
                    });
                }
            }
        }

        if (!isLimit)
        {
            await next.Invoke();
        }
    }
}

上述代码说明:sem.WaitOne(0)这个超时时间,最好是0,即不等待,否则高并发下会有问题。SemaphoreSlim的异步wait没试过。如果超时时间大于0,意味着,高并发下,会有大量的等待,异步等待也是等待。
SemaphoreSlim短时间是自旋,想象一下一瞬间产生大量自旋会怎么样?所以最好不等待,如果要等待,那代码还得再研究研究,经过测试才能用。

6. 注册拦截器

//拦截器
builder.Services.AddMvc(options =>
{
    ...省略

    options.Filters.Add<RateLimitFilter>();
});

六. 使用jMeter进行压力测试

测试结果:

  1. 被限速的接口,满足限速条件的调用并发量大时,部分用户成功,部分用户提示当前查询的人多请稍后再试。但不影响未满足限速条件的传参调用,也不影响其它未限速接口的调用。
  2. 测试的所有接口、所有查询参数条件的调用,耗时稳定,大量并发时,不会出现接口耗时几十秒甚至1分钟的情况。

七. 同时测试三个接口

测试三个接口,一个是触发限流的A接口,一个是未触发限流的A接口,一个是未被限流的B接口。

jMeter测试设置

触发限流的A接口,并发量设置为200:

未触发限流的A接口以及未被限流的B接口,并发量设置为1:

测试日志截图


截图说明:可以看到被限流接口共1000次调用,只有大约40次调用是成功的,剩下的返回请稍后再试。


截图说明:实际上触发限流的接口,并发量为8,压力依然很大,会拖慢自身以及其它接口,当触发限流的接口请求结束时,其它接口访问速度才正常。

八. 实际情况

  1. 这种接口计算量大,是难以支持高并发的,需要限流。争取客户的理解,仅支持少量用户在同一时间查询。
  2. 实际上只要用户错开几秒访问,接口的耗时就很正常。问题是,如何错开几秒呢?当用户看到”请稍后再试”的提示,关闭提示,重新点击查询,就可以错开了。如果一次两次不行,就多点几次查询。

九. 后续

  1. 修改为使用SemaphoreSlim类,这样可以异步等待
  2. RateLimitAttribute类增加了超时时间属性

代码如下:

1. RateLimitAttribute

/// <summary>
/// 接口限速
/// </summary>
public class RateLimitAttribute : Attribute
{
    private SemaphoreSlim _sem;

    public SemaphoreSlim Sem
    {
        get
        {
            return _sem;
        }
    }

    /// <summary>
    /// 超时时间(单位:毫秒)
    /// </summary>
    private int _timeout;

    /// <summary>
    /// 超时时间(单位:毫秒)
    /// </summary>
    public int Timeout
    {
        get
        {
            return _timeout;
        }
    }

    /// <summary>
    /// 接口限速
    /// </summary>
    /// <param name="limitCount">限制并发数量</param>
    /// <param name="timeout">超时时间(单位:秒)</param>
    public RateLimitAttribute(int limitCount = 1, int timeout = 0)
    {
        _sem = new SemaphoreSlim(limitCount, limitCount);
        _timeout = timeout * 1000;
    }
}

2. RateLimitFilter

/// <summary>
/// 接口限速
/// </summary>
public class RateLimitFilter : ActionFilterAttribute
{
    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        Type controllerType = context.Controller.GetType();
        object arg = context.ActionArguments.Values.ToList()[0];
        var rateLimit = context.ActionDescriptor.EndpointMetadata.OfType<RateLimitAttribute>().FirstOrDefault();

        bool isLimit = false;
        if (rateLimit != null && arg is RateLimitInterface)
        {
            RateLimitInterface model = arg as RateLimitInterface;
            if (model.IsLimit) //满足限速条件
            {
                isLimit = true;
                SemaphoreSlim sem = rateLimit.Sem;

                if (await sem.WaitAsync(rateLimit.Timeout))
                {
                    try
                    {
                        await next.Invoke();
                    }
                    catch
                    {
                        throw;
                    }
                    finally
                    {
                        sem.Release();
                    }
                }
                else
                {
                    var routeList = context.RouteData.Values.Values.ToList();
                    routeList.Reverse();
                    var route = string.Join('/', routeList.ConvertAll(a => a.ToString()));
                    var msg = $"当前访问{route}接口的用户数太多,请稍后再试";
                    LogUtil.Info(msg);
                    context.Result = new ObjectResult(new ApiResult
                    {
                        code = (int)HttpStatusCode.ServiceUnavailable,
                        message = "当前查询的用户太多,请稍后再试。"
                    });
                }
            }
        }

        if (!isLimit)
        {
            await next.Invoke();
        }
    }
}

效果

  1. 假如设置RateLimit(1, 0),即并发1,超时时间0,那么当100个并发请求时,只有1个成功,99个失败。
  2. 假如接口耗时2秒,设置RateLimit(1, 10),即并发1,超时时间10秒,那么当100个并发请求时,会有大约5个成功,95个失败。第1个成功的接口请求耗时大约2秒,后续成功的4个,请求耗时依次增加。
  3. 当设置了并发量和超时时间后,接口平均一秒钟能被请求多少次,取决于接口耗时,耗时短的接口平均每秒能被请求的次数多,耗时长的接口平均每秒能被请求的次数少。

 PHP: ThinkPHP获取客户端IP地址_thinkphp 获取ip_彭世瑜的博客-CSDN博客

mikel阅读(413)

来源: (1条消息) PHP: ThinkPHP获取客户端IP地址_thinkphp 获取ip_彭世瑜的博客-CSDN博客

1、ThinkPHP5通过助手函数即可获取

request()->ip()
1
2、通过自己解析

function get_client_ip($type = 0)
{
$type = $type ? 1 : 0;
static $ip = NULL;

if ($ip !== NULL) {
return $ip[$type];
}

if (isset($_SERVER[‘HTTP_X_REAL_IP’])) {
//nginx 代理模式下,获取客户端真实IP
$ip = $_SERVER[‘HTTP_X_REAL_IP’];
} elseif (isset($_SERVER[‘HTTP_CLIENT_IP’])) {
//客户端的ip
$ip = $_SERVER[‘HTTP_CLIENT_IP’];
} elseif (isset($_SERVER[‘HTTP_X_FORWARDED_FOR’])) {
//浏览当前页面的用户计算机的网关
$arr = explode(‘,’, $_SERVER[‘HTTP_X_FORWARDED_FOR’]);
$pos = array_search(‘unknown’, $arr);
if (false !== $pos) unset($arr[$pos]);
$ip = trim($arr[0]);
} elseif (isset($_SERVER[‘REMOTE_ADDR’])) {
//浏览当前页面的用户计算机的ip地址
$ip = $_SERVER[‘REMOTE_ADDR’];
} else {
$ip = $_SERVER[‘REMOTE_ADDR’];
}
// IP地址合法验证
$long = sprintf(“%u”, ip2long($ip));
$ip = $long ? array($ip, $long) : array(‘0.0.0.0’, 0);
return $ip[$type];
}

参考
获取客户端IP地址
————————————————
版权声明:本文为CSDN博主「彭世瑜」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/mouday/article/details/121626509

ThinkPHP获取客户端IP地址

mikel阅读(330)

ThinkPHP中有一个自带的函数,用于获取客户端计算机的IP地址:get_client_ip()。

调用的方式很简单:
$client_ip = get_client_ip();

除了ThinkPHP内置get_client_ip函数外,也可使用下面函数获取客户端IP地址。

/**
* 获取客户端的IP地址
* @param $type 表示返回类型 0 返回IP地址, 1 返回IPV4地址数字
*/

function get_client_ip($type = 0) {
$type       =  $type ? 1 : 0;
static $ip  =   NULL;
if ($ip !== NULL) return $ip[$type];
if (isset($_SERVER[‘HTTP_X_FORWARDED_FOR’])) {
$arr    =   explode(‘,’, $_SERVER[‘HTTP_X_FORWARDED_FOR’]);
$pos    =   array_search(‘unknown’,$arr);
if(false !== $pos) unset($arr[$pos]);
$ip     =   trim($arr[0]);
}elseif (isset($_SERVER[‘HTTP_CLIENT_IP’])) {
$ip     =   $_SERVER[‘HTTP_CLIENT_IP’];
}elseif (isset($_SERVER[‘REMOTE_ADDR’])) {
$ip     =   $_SERVER[‘REMOTE_ADDR’];
}
// IP地址合法验证
$long = ip2long($ip);
$ip   = $long ? array($ip, $long) : array(‘0.0.0.0’, 0);
return $ip[$type];
}

thinkphp I()方法获取不到ajax传值 - 蜡笔没有小新 - 博客园

mikel阅读(376)

来源: thinkphp I()方法获取不到ajax传值 – 蜡笔没有小新 – 博客园

把json数据post给PHP,但在PHP里面$_post获取不到,I(‘id’)也获取不到,$_REQUEST也获取不到,可以看下ajax的contentType设置的数据类型是什么,php对对型如text/xml 或者 soap 或者 application/octet-stream 之类的内容无法解析,原来PHP默认只识别application/x-www.form-urlencoded标准的数据类型,我们只要将ajax的contentType设置成application/x-www.form-urlencoded就可以,或者使用file_get_contents(“php://input”)。下面我们在来看TP中的I()方法是如何做的?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
function I($name,$default='',$filter=null) {
    if(strpos($name,'.')) { // 指定参数来源
        //判断参数$name中是否包括.号
        list($method,$name) =   explode('.',$name,2);
        //如果包括.号将.号前后分隔,并且分别赋值给$method以及$name
    }else// 默认为自动判断
        //如果没有.号
        $method =   'param';
    }
    switch(strtolower($method)) {//将$method转换为小写
        //如果$method为get,则$input为$_GET
        case 'get'     :   $input =& $_GET;break;
        //如果$method为get,则$input为$_POST
        case 'post'    :   $input =& $_POST;break;
        //如果为put,则将post的原始数据转参数给$input
        case 'put'     :   parse_str(file_get_contents('php://input'), $input);break;
        //如果是param
        case 'param'   :
            //判断$_SERVER['REQUEST_METHOD']
            switch($_SERVER['REQUEST_METHOD']) {
                //如果为post,则$input的内容为$_POST的内容
                case 'POST':
                    $input  =  $_POST;
                    break;
                //如果为PUT.则input的内容为PUT的内容
                case 'PUT':
                    parse_str(file_get_contents('php://input'), $input);
                    break;
                //默认为$_GET的内容
                default:
                    $input  =  $_GET;
            }
            break;
        //如果$method为request,则$input为$_REQUEST
        case 'request' :   $input =& $_REQUEST;   break;
        //如果$method为session,则$input为$_SESSION
        case 'session' :   $input =& $_SESSION;   break;
        //如果$method为cookie,则$input为$_COOKIE
        case 'cookie'  :   $input =& $_COOKIE;    break;
        //如果$method为server,则$input为$_SERVER
        case 'server'  :   $input =& $_SERVER;    break;
        //如果$method为globals,则$input为$GLOBALS
        case 'globals' :   $input =& $GLOBALS;    break;
        //默认返回空
        default:
            return NULL;
    }
    /**
     * 到此为止,已经根据传入的参数的需要(第一个参数.号前面的),把所有的变量都取到了。下面就是返回变量的内容了。
    **/
    //如果$name为空,也就是I()第一个参数的.号后面为空的时候
    if(empty($name)) { // 获取全部变量
        //获取到的变量$input全部复制给$data
        $data       =   $input;
        //array_walk_recursive — 对数组中的每个成员递归地应用用户函数
        //将$data的键值作为filter_exp函数的第一个参数,键名作为第二个参数
        //如果$data的键值中含有or或者exp这两个字符,自动在后面加一个空格
        array_walk_recursive($data,'filter_exp');
        //判断过滤参数是否有,如果有的话,就直接使用过滤方法,如果没有的话,就使用配置中的过滤方法
        $filters    =   isset($filter)?$filter:C('DEFAULT_FILTER');
        if($filters) {
            $filters    =   explode(',',$filters);
            //将过滤参数中的每个方法都应用到$data中
            foreach($filters as $filter){
                //将$data的每个值使用$filters过滤
                $data   =   array_map_recursive($filter,$data); // 参数过滤
            }
        }
    }elseif(isset($input[$name])) { // 取值操作
        $data       =   $input[$name];
        is_array($data) && array_walk_recursive($data,'filter_exp');
        $filters    =   isset($filter)?$filter:C('DEFAULT_FILTER');
        if($filters) {
            $filters    =   explode(',',$filters);
            foreach($filters as $filter){
                if(function_exists($filter)) {
                    $data   =   is_array($data)?array_map_recursive($filter,$data):$filter($data); // 参数过滤
                }else{
                    $data   =   filter_var($data,is_int($filter)?$filter:filter_id($filter));
                    if(false === $data) {
                        return   isset($default)?$default:NULL;
                    }
                }
            }
        }
    }else// 变量默认值
        $data       =    isset($default)?$default:NULL;
    }
    return $data;
}

从源码中我们可以看到I方法的$method分为get、post、put、param,当ajax的contentType不是标准类型时,在我们接受值时可以写成

1
I('put.id')

就可以正常接收到值了

sql中2中分页 row_number() 和OFFSET, FETCH 分页(2012特有)_飞天海里鱼的博客-CSDN博客

mikel阅读(491)

来源: sql中2中分页 row_number() 和OFFSET, FETCH 分页(2012特有)_飞天海里鱼的博客-CSDN博客

第一个种分页,使用row_number()  over(order by)

比如:

CREATE TABLE [dbo].[Student](
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NULL

)

 

declare @pageIndex int=3
declare @pageSize int=5

select * from

(

select ROW_NUMBER() over(order by ID) as numbers,ID,name from student

)  c where numbers between (@pageIndex-1)*@pageSize+1 and @pageIndex*@pageSize

  1. ORDER BY order_by_expression
  2. [ COLLATE collation_name ]
  3. [ ASC | DESC ]
  4. [ ,…n ]
  5. [ <offset_fetch> ]
  6. <offset_fetch> ::=
  7. {
  8. OFFSET { integer_constant | offset_row_count_expression } { ROW | ROWS }
  9. [
  10. FETCH { FIRST | NEXT } {integer_constant | fetch_row_count_expression } { ROW | ROWS } ONLY
  11. ]

第二种

select * from student order by ID OFFSET (@pageIndex-1)*@pageSize ROW FETCH next @pageSize rows only

推荐采用第二种

SQL分页查询:offset xxx rows fetch next xxx rows only 方式_陈皮糖chen的博客-CSDN博客

mikel阅读(497)

来源: SQL分页查询:offset xxx rows fetch next xxx rows only 方式_陈皮糖chen的博客-CSDN博客

分页:offset xxx rows fetch next xxx rows only 方式
分页实现的方法又多了一种,在SQL Server 2012版本中,TSQL在Order By子句中新增 Offset-Fetch子句,用于从有序的结果集中,跳过一定数量的数据行,获取指定数量的数据行,从而达到数据行分页的目的。经过测试,从逻辑读取数量和响应的时间消耗来测评,使用Offset-Fetch实现的分页方式,比Row_Number()方式性能要高很多。

Offset-Fetch子句要求结果集是有序的,因此,只能用于order by 子句中,语法如下:

// x1:跳过的行数 x2:显示的行数
ORDER BY 字段 ASC/DESC offset x1 rows fetch next x2 rows only例如:

// LEN(字段名)取长度 @pageIndex:第几页 @pageLine:一页显示的条数
// 假如一页显示3条数据,查询第2页需要显示的数据,则
// ((@pageIndex – 1) * @pageLine)=(2-1)*3 @pageLine=3
ORDER BY LEN(MW.WREA_CODE),MW.WREA_CODE
offset((@pageIndex – 1) * @pageLine) rows fetch next @pageLine rows only

关键字解析:
•Offset子句:用于指定跳过(Skip)的数据行;
•Fetch子句:该子句在Offset子句之后执行,表示在跳过(Sikp)指定数量的数据行之后,返回一定数据量的数据行;
•执行顺序:Offset子句必须在Order By 子句之后执行,Fetch子句必须在Offset子句之后执行;

分页实现的思路:
1.在分页实现中,使用Order By子句,按照指定的columns对结果集进行排序;
2.使用Offset子句跳过前N页:Offset (@PageIndex-1)*@RowsPerPage rows;
3.使用Fetch子句呈现当前Page:Fetch next @RowsPerPage rows only;
————————————————
版权声明:本文为CSDN博主「陈皮糖chen」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_45659376/article/details/107336143

SQL中用OFFSET FETCH NEXT 分页的坑__学而时习之_的博客-CSDN博客

mikel阅读(387)

来源: (1条消息) SQL中用OFFSET FETCH NEXT 分页的坑__学而时习之_的博客-CSDN博客

SQL2012后的版本中支持了OFFSET index FETCH NEXT page_size ROWS ONLY的分页方式,但要分页就必须要有排序,而排序的字段选择的不对,就有可能造成分页结果不正确,比如第1页和第2页的数据有重复。为什么呢?

究其原因,是因为排序字段的数据不唯一,或才有null,比如按价格排序,有同一价格的商品很多,结果就会造成排序错误。所以需要选择唯一值的字段来排序,比如自增的索引、GUID、KEY等,示例代码如下

DECLARE @PageIndex INT
DECLARE @PageSize INT
SET @PageIndex=0
SET @PageSize=10
SELECT * FROM TProduct ORDER BY Product_Key DESC
OFFSET @PageIndex * @PageSize ROWS FETCH NEXT @PageSize ROWS ONLY

————————————————
版权声明:本文为CSDN博主「_学而时习之_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xxdddail/article/details/89173470

在excel如何分割字符串?-excel-PHP中文网

mikel阅读(360)

来源: 在excel如何分割字符串?-excel-PHP中文网

在excel如何分割字符串?

方法:

一、首先先输入一组要拆分字符串的数据,全部选中。

e4fa881ee68ba4a623a3076cccf8924.png

二、然后在上方菜单找到“分列”,点击选中。

3c3fe8c594f4df80dad022ae088864a.png

三、然后在弹出的“文本分列向导”中 选择“分隔符号”下一步。

15e62224919730ff5cedbb007a53233.png

四、然后在“分隔符号”位置选择“空格”,点击选择下一步。

1db1364ea66331d42b1de4a000829f5.png

五、然后在第三步中选择打“列数据格式”选择则“文本”然后点击选择“确定”

168175b2d2f3ecde1d75aadcac4dece.png

六、EXCEL分割字符串完成

506148b0840f74c64070492ed8baf82.png

推荐教程:《excel教程

以上就是在excel如何分割字符串?的详细内容,更多请关注php中文网其它相关文章!

低代码 系列—— 初步认识 Appsmith - 彭加李 - 博客园

mikel阅读(638)

来源: 低代码 系列—— 初步认识 Appsmith – 彭加李 – 博客园

初步认识 Appsmith

appsmith 是什么

appsmith 是 github 上的一个开源项目,截至此刻(20220512)有 17.7k Star。

Appsmith 是一个低代码、开源的框架,用于构建内部应用程序

使用基于 JavaScript可视化开发平台,构建 CRUD 应用程序、仪表盘、管理面板,而且速度快了 10 倍。

Tip:With our JavaScript-based visual development platform, build CRUD apps, Dashboards, Admin Panels, and many more 10x faster. —— docs_Introduction

安装 appsmith

:笔者使用的 win10

打开官网,有两段醒目的文字:

A powerful open source framework to build internal tools
用于构建内部工具的强大开源框架

Quickly build any custom business software with pre-built UI widgets that connect to any data source. Control everything with JavaScript.
使用可连接到任何数据源的预构建 UI 小部件快速构建任何自定义业务软件。 使用 JavaScript 控制一切

点击docHost it yourself,进入文档

appsmith100.png

Appsmith 提供了多种技术部署。笔者选用 Docker。

Tip

  • 唯一官方支持的 Appsmith 安装是基于 Docker 的
  • Appsmith 可以在本地部署,也可以使用 Docker 在您的私有实例上部署 —— docs_Docker
  • 倘若不了解 docker,可以查看笔者的 初步认识 docker

进入 setup/docker 安装指南:

提供了两种安装方式:

  • Quick Start (with docker-compose) – 使用 docker-compose。
  • Explore Appsmith (without docker-compose) – 不使用 docker-compose。

docker compose 用于构建和管理多个服务,更复杂,这里笔者选用更简单的方式:without docker-compose

通过 docker run 下载镜像并启动 Appsmith:

// 约1.2G
202205-later> docker run -d --name appsmith -p 80:80 -v "$PWD/stacks:/appsmith-stacks" appsmith/appsmith-ce
Unable to find image 'appsmith/appsmith-ce:latest' locally
latest: Pulling from appsmith/appsmith-ce
8e5c1b329fe3: Pull complete
c086a11e6410: Pull complete
77fbce06aba6: Pull complete
01e01a36d9f0: Pull complete
525e27e69b74: Pull complete
f23d2a639a69: Pull complete
39c9347cc360: Pull complete
cfdc8301afe2: Pull complete
f496d56b0e53: Pull complete
45e7897ce8f4: Pull complete
e4fa2a7eeac0: Pull complete
1ece9193ec88: Pull complete
2b90261d42de: Pull complete
72ad69fc9113: Pull complete
84c90c9c8dfc: Pull complete
60270c8d4298: Pull complete
df215547aa3b: Pull complete
0d8252e94cfe: Pull complete
c1494763999c: Pull complete
367d490330fe: Pull complete
4f4fb700ef54: Pull complete
755c6060309a: Pull complete
79f8c7decfae: Pull complete
Digest: sha256:e34adcdf4fade53440d8406753078d6b0a7cbd7ef73d73747e4bf0274b34fc6f
Status: Downloaded newer image for appsmith/appsmith-ce:latest
0018628962f2a8df3068b6597a91a9529cdcf39cd0497309698fc176ced5fb6f

通过 docker ps 查看运行中的容器,发现 appsmith 已启动:
已启动:

202205-later> docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED         STATUS         PORTS                                                 NAMES
0018628962f2   appsmith/appsmith-ce   "/opt/appsmith/entry…"   2 minutes ago   Up 2 minutes   0.0.0.0:80->80/tcp, 0.0.0.0:9001->9001/tcp, 443/tcp   appsmith

浏览器访问 localhost:80,跳转至 Welcome 页面:

appsmith1.png

点击GET STARTED设置账户(即注册),进入下图:

appsmith2.png

输入信息后,点击NEXT,进入下图:

appsmith3.png

点击MAKE ... APP按钮,进入下图:

appsmith4.png

点击BUILD ... OWN,进入 appsmith,如下图所示:

appsmith6.png

整个界面分左中右三部分:

  • 左侧,有项目的页面(pages);部件(widgets),例如按钮、表格;查询(queries),例如刷新表格数据、数据库相关(datasources)
  • 中央,项目的ui
  • 右侧,配置,例如配置按钮的颜色、显示文字、事件等

hello world

需求:点击按钮,弹出信息

做法是:

  • 左侧拖拽 BUTTON 部件到舞台中央
  • 点击舞台中央的按钮
  • 在右侧进行配置,例如修改Label、添加事件(onClick)等

appsmith-btn1.png

点击舞台中央的按钮,或右上角的DEPLOY(部署)进行测试。结果如下图所示:

appsmith-btn2.png

连接本地数据库

笔者的本地数据库采用 mySQL

据官网介绍,在 appsmith 中使用本地 api,需要使用 ngrok —— 如何在 Appsmith 上使用本地 API

Tipngrok 是将您的应用程序放到互联网上的最快方式。

安装 mySQL

首先下载 mySQL。笔者进入这里,直接点击No thanks, just start my download.下载 .msi 版本。如下图所示:

mysql0.png

双击 .msi 版本安装,未做特殊配置,安装过程需要等待一些时间下载(笔者花了20来分钟):

mysql.png

进入 mysql 工作台:

mysql2.png

:实例名称笔者是 ”MySQL80“

安装 Navicat for MySQL

Navicat for MySQL 为数据库管理供了直观而强大的图形界面。

直接来 这里 下载安装即可

启动,然后新建 mysql 的连接,输入相关信息。如下图所示:
mysql3.png

点击Test Connection测试连接

mysql4.png

连接成功!

新建一个数据库 test-database,再创建一个表 users,定义4个字段。如下图所示:

mysql5.png

点击 Test Connection 测试连接,报错如下:

client does not support authentication protocol requested by server; consider upgrading MySQL client

笔者依次输入下面三个命令,再次测试即可通过:

mysql> use mysql;

mysql> alter user 'root'@'localhost' identified with mysql_native_password by '你的密码';
Query OK, 0 rows affected, 1 warning (0.02 sec)

mysql> flush privileges;
Query OK, 0 rows affected (0.01 sec)

安装 ngrok

进入 ngrok 官网

首先进行免费注册,然后就能获取授权码,用于连接你的账号。如下图所示:

ngrok.png

Tip:注册好像需要能FQ,笔者使用 github 账号授权完成注册。

然后进入官网的 Download 模块下载 wins 版本,解压即可:

D:\software
// 下载的源文件
2022/05/03  23:00         7,261,772 ngrok-v3-stable-windows-amd64.zip
// 解压
2022/04/27  07:02        17,821,696 ngrok.exe

双击 ngrok.exe,然后输入授权码相关代码连接账号:

software>ngrok config add-authtoken 28g6uez9gLpfamK1zG6j81SioFY_849x4eb96MNpQLaot5naj
Authtoken saved to configuration file: C:\Users\77714\AppData\Local/ngrok/ngrok.yml

最后提供本地网络应用程序:

software>ngrok tcp 3306

ngrok2.png

:两处红框的信息稍后会在 appsmith 连接数据源时使用。重启后,端口 17872 也会改变。

创建数据源

Appsmith 选择 mysql 数据源。如下图所示:

mysql6.png

然后输入相关信息,点击Test测试通过。如下图所示:

appsmith7.png

创建成功!

:倘若重启 ngrok(例如重启机器了),这里的端口(17872)需要重新配置,因为端口变了,否则测试不会再通过。

创建查询

点击NEW QUERY...新建查询。如下图所示:

appsmith8.png

点击Select。如下图所示:

appsmith9.png

Tip:上图的 CreateSelectUpdateDelete 是否就是对应官网描述的 CURD

With our JavaScript-based visual development platform, build CRUD apps, Dashboards, Admin Panels, and many more 10x faster.

使用我们基于 JavaScript 的可视化开发平台,构建 CRUD 应用程序、仪表板、管理面板等的速度提高 10 倍。

修改代码为SELECT * FROM users,用于查询我们的表。然后点击右上角的RUN,数据即可同步过来。如下图所示:

appsmith10.png

Tip:笔者先前已在数据库中插入了这条记录

Table Widgets

拖拽表格部件,输入{{}}根据提示配置表格数据。如下图所示:

appsmith11.png

刷新表格

appsmith12.png

内网部署 Appsmith

需求:将 Appsmit 部署到内网的 ubuntu 20.04 服务器

步骤如下:

在外网环境中下载镜像并导出 .tar 文件:

// 在外网环境中下载镜像
$ docker pull appsmith/appsmith-ce

// 导出镜像
$ docker save appsmith/appsmith-ce -o appsmith_appsmith_ce.tar

把导出的 .tar 文件弄到的 ubuntu 服务器中,然后导入镜像,最后运行即可:

// 导入镜像
$ docker load -i appsmith_appsmith_ce.tar

// 运行镜像 appsmith/appsmith-ce
$ docker run -d --name appsmith -p 80:80 -v "$PWD/stacks:/appsmith-stacks" appsmith/appsmith-ce

浏览器访问 localhost:80,后续就和上文相同。比如注册信息也没有什么需要注意的。

其他章节请看:

低代码 系列