精华内容
下载资源
问答
  • PHP 中使用参数查询

    2021-01-19 01:00:23
    扩展mysqlmysqliPDOPHP 版本2.0+5.0+5.1+生命周期废弃活跃活跃面向对象语法否是是过程式语法是是否服务器端预处理语句否是是客户端预处理语句否否是上面所说的预处理语句就是用于参数查询的。可以看到,除了旧的 ...

    PHP 中提供了三种访问 MySQL 数据库的扩展,即 mysql,mysqli 和 PDO。它们的区别可以比较如下:

    扩展

    mysql

    mysqli

    PDO

    PHP 版本

    2.0+

    5.0+

    5.1+

    生命周期

    废弃

    活跃

    活跃

    面向对象语法

    过程式语法

    服务器端预处理语句

    客户端预处理语句

    上面所说的预处理语句就是用于参数化查询的。可以看到,除了旧的 mysql 扩展不支持,mysqli 和 PDO 这两个新扩展都支持参数化查询。PDO 扩展相比 mysqli 扩展的好处是,它是与关系数据库类型无关的,因此很方便切换数据库,比如从 MySQL 切换到 PostgreSQL。

    首先我们来看看利用 mysqli 扩展如何使用参数化查询。例如:

    $mysqli = new mysqli("localhost", "dbusername", "dbpassword", "database");

    $username = "somename";

    $password = "someword";

    $query = "SELECT filename, filesize FROM users WHERE (name = ?) and (password = ?)";

    $stmt = $mysqli->stmt_init();

    if ($stmt->prepare($query)) {

    $stmt->bind_param("ss", $username, $password);

    $stmt->execute();

    $stmt->bind_result($filename, $filesize);

    while ($stmt->fetch()) {

    printf ("%s : %d\n", $filename, $filesize);

    }

    $stmt->close();

    }

    $mysqli->close();

    再看看用 PDO 扩展如何使用参数化查询。例如:

    $pdo = new PDO("mysql:host=localhost;dbname=database", "dbusername", "dbpassword");

    $username = "somename";

    $password = "someword";

    $query = "SELECT * FROM users WHERE (name = :username) and (password = :password)";

    $statement = $pdo->prepare($query, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));

    $statement->bindParam(":username", $username, PDO::PARAM_STR, 10);

    $statement->bindParam(":password", $password, PDO::PARAM_STR, 12);

    $statement->execute();

    while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {

    printf ("%s : %d\n", $row["filename"], $row["filesize"]);

    }

    $statement->closeCursor();

    $pdo = null;

    展开全文
  • 若有一天你不可避免的需要提高SQL的查询性能,需要一次性where in 几百、上千、甚至上万条数据时,参数查询将是必然进行的选择身为一名小小的程序猿,在日常开发中不可以避免的要和where in和like打交道,在大多数...

    若有一天你不可避免的需要提高SQL的查询性能,需要一次性where in 几百、上千、甚至上万条数据时,参数化查询将是必然进行的选择

    身为一名小小的程序猿,在日常开发中不可以避免的要和where in和like打交道,在大多数情况下我们传的参数不多简单做下单引号、敏感字符转义之后就直接拼进了SQL,执行查询,搞定。若有一天你不可避免的需要提高SQL的查询性能,需要一次性where in 几百、上千、甚至上万条数据时,参数化查询将是必然进行的选择。然而如何实现where in和like的参数化查询,是个让不少人头疼的问题。

    where in 的参数化查询实现

    首先说一下我们常用的办法,直接拼SQL实现,一般情况下都能满足需要

    代码如下:

    string userIds = "1,2,3,4";

    using (SqlConnection conn = new SqlConnection(connectionString))

    {

    conn.Open();

    SqlCommand comm = new SqlCommand();

    comm.Connection = conn;

    comm.CommandText = string.Format("select * from Users(nolock) where UserID in({0})", userIds);

    comm.ExecuteNonQuery();

    }

    需要参数化查询时进行的尝试,很显然如下这样执行SQL会报错错误

    代码如下:

    using (SqlConnection conn = new SqlConnection(connectionString))

    {

    conn.Open();

    SqlCommand comm = new SqlCommand();

    comm.Connection = conn;

    comm.CommandText = "select * from Users(nolock) where UserID in(@UserID)";

    comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value = "1,2,3,4" });

    comm.ExecuteNonQuery();

    }

    很显然这样会报错误:在将 varchar 值 '1,2,3,4' 转换成数据类型 int 时失败,因为参数类型为字符串,where in时会把@UserID当做一个字符串来处理,相当于实际执行了如下语句

    代码如下:

    select * from Users(nolock) where UserID in('1,2,3,4')

    若执行的语句为字符串类型的,SQL执行不会报错,当然也不会查询出任何结果

    代码如下:

    using (SqlConnection conn = new SqlConnection(connectionString))

    {

    conn.Open();

    SqlCommand comm = new SqlCommand();

    comm.Connection = conn;

    comm.CommandText = "select * from Users(nolock) where UserName in(@UserName)";

    comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar, -1) { Value = "'john','dudu','rabbit'" });

    comm.ExecuteNonQuery();

    }

    这样不会抱任何错误,也查不出想要的结果,因为这个@UserName被当做一个字符串来处理,实际相当于执行如下语句

    代码如下:

    select * from Users(nolock) where UserName in('''john'',''dudu'',''rabbit''')

    由此相信大家对于为何简单的where in 传参无法得到正确的结果知道为什么了吧,下面我们来看一看如何实现正确的参数化执行where in,为了真正实现参数化where in 传参,很多淫才想到了各种替代方案

    方案1,使用CHARINDEX或like 方法实现参数化查询,毫无疑问,这种方法成功了,而且成功的复用了查询计划,但同时也彻底的让查询索引失效(在此不探讨索引话题),造成的后果是全表扫描,如果表里数据量很大,百万级、千万级甚至更多,这样的写法将造成灾难性后果;如果数据量比较小、只想借助参数化实现防止SQL注入的话这样写也无可厚非,还是得看具体需求。(不推荐)

    代码如下:

    using (SqlConnection conn = new SqlConnection(connectionString))

    {

    conn.Open();

    SqlCommand comm = new SqlCommand();

    comm.Connection = conn;

    //使用CHARINDEX,实现参数化查询,可以复用查询计划,同时会使索引失效

    comm.CommandText = "select * from Users(nolock) where CHARINDEX(','+ltrim(str(UserID))+',',','+@UserID+',')>0";

    comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value = "1,2,3,4" });

    comm.ExecuteNonQuery();

    }

    using (SqlConnection conn = new SqlConnection(connectionString))

    {

    conn.Open();

    SqlCommand comm = new SqlCommand();

    comm.Connection = conn;

    //使用like,实现参数化查询,可以复用查询计划,同时会使索引失效

    comm.CommandText = "select * from Users(nolock) where ','+@UserID+',' like '%,'+ltrim(str(UserID))+',%' ";

    comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value = "1,2,3,4" });

    comm.ExecuteNonQuery();

    }

    方案2 使用exec动态执行SQL,这样的写法毫无疑问是很成功的,而且代码也比较优雅,也起到了防止SQL注入的作用,看上去很完美,不过这种写法和直接拼SQL执行没啥实质性的区别,查询计划没有得到复用,对于性能提升没任何帮助,颇有种脱了裤子放屁的感觉,但也不失为一种解决方案。(不推荐)

    代码如下:

    using (SqlConnection conn = new SqlConnection(connectionString))

    {

    conn.Open();

    SqlCommand comm = new SqlCommand();

    comm.Connection = conn;

    //使用exec动态执行SQL

    //实际执行的查询计划为(@UserID varchar(max))select * from Users(nolock) where UserID in (1,2,3,4)

    //不是预期的(@UserID varchar(max))exec('select * from Users(nolock) where UserID in ('+@UserID+')')

    comm.CommandText = "exec('select * from Users(nolock) where UserID in ('+@UserID+')')";

    comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value = "1,2,3,4" });

    comm.ExecuteNonQuery();

    }

    方案3 为where in的每一个参数生成一个参数,写法上比较麻烦些,传输的参数个数有限制,最多2100个,可以根据需要使用此方案(推荐)

    代码如下:

    using (SqlConnection conn = new SqlConnection(connectionString))

    {

    conn.Open();

    SqlCommand comm = new SqlCommand();

    comm.Connection = conn;

    //为每一条数据添加一个参数

    comm.CommandText = "select * from Users(nolock) where UserID in (@UserID1,@UserId2,@UserID3,@UserID4)";

    comm.Parameters.AddRange(

    new SqlParameter[]{

    new SqlParameter("@UserID1", SqlDbType.Int) { Value = 1},

    new SqlParameter("@UserID2", SqlDbType.Int) { Value = 2},

    new SqlParameter("@UserID3", SqlDbType.Int) { Value = 3},

    new SqlParameter("@UserID4", SqlDbType.Int) { Value = 4}

    });

    comm.ExecuteNonQuery();

    }

    方案4 使用临时表实现(也可以使用表变量性能上可能会更加好些),写法实现上比较繁琐些,可以根据需要写个通用的where in临时表查询的方法,以供不时之需,个人比较推崇这种写法,能够使查询计划得到复用而且对索引也能有效的利用,不过由于需要创建临时表,会带来额外的IO开销,若查询频率很高,每次的数据不多时还是建议使用方案3,若查询数据条数较多,尤其是上千条甚至上万条时,强烈建议使用此方案,可以带来巨大的性能提升(强烈推荐)

    代码如下:

    using (SqlConnection conn = new SqlConnection(connectionString))

    {

    conn.Open();

    SqlCommand comm = new SqlCommand();

    comm.Connection = conn;

    string sql = @"

    declare @Temp_Variable varchar(max)

    create table #Temp_Table(Item varchar(max))

    while(LEN(@Temp_Array) > 0)

    begin

    if(CHARINDEX(',',@Temp_Array) = 0)

    begin

    set @Temp_Variable = @Temp_Array

    set @Temp_Array = ''

    end

    else

    begin

    set @Temp_Variable = LEFT(@Temp_Array,CHARINDEX(',',@Temp_Array)-1)

    set @Temp_Array = RIGHT(@Temp_Array,LEN(@Temp_Array)-LEN(@Temp_Variable)-1)

    end

    insert into #Temp_Table(Item) values(@Temp_Variable)

    end

    select * from Users(nolock) where exists(select 1 from #Temp_Table(nolock) where #Temp_Table.Item=Users.UserID)

    drop table #Temp_Table";

    comm.CommandText = sql;

    comm.Parameters.Add(new SqlParameter("@Temp_Array", SqlDbType.VarChar, -1) { Value = "1,2,3,4" });

    comm.ExecuteNonQuery();

    }

    like参数化查询

    like查询根据个人习惯将通配符写到参数值中或在SQL拼接都可,两种方法执行效果一样,在此不在详述

    代码如下:

    using (SqlConnection conn = new SqlConnection(connectionString))

    {

    conn.Open();

    SqlCommand comm = new SqlCommand();

    comm.Connection = conn;

    //将 % 写到参数值中

    comm.CommandText = "select * from Users(nolock) where UserName like @UserName";

    comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar, 200) { Value = "rabbit%" });

    comm.ExecuteNonQuery();

    }

    using (SqlConnection conn = new SqlConnection(connectionString))

    {

    conn.Open();

    SqlCommand comm = new SqlCommand();

    comm.Connection = conn;

    //SQL中拼接 %

    comm.CommandText = "select * from Users(nolock) where UserName like @UserName+'%'";

    comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar, 200) { Value = "rabbit%" });

    comm.ExecuteNonQuery();

    }

    看到Tom.汤和蚊子额的评论 补充了下xml传参和tvp传参,并对6种方案做了个简单总结

    此文章属懒惰的肥兔原创

    本文原创发布php中文网,转载请注明出处,感谢您的尊重!

    相关文章

    相关视频

    展开全文
  • 本文转自:http://www.cnblogs.com/hnsdwhl/archive/2011/07/23/2114730.html当需要根据外部输入的参数来决定要执行的SQL语句时,常常需要动态来构造SQL查询语句,个人觉得用得比较多的地方就是分页存储过程和执行...

    本文转自:http://www.cnblogs.com/hnsdwhl/archive/2011/07/23/2114730.html

    当需要根据外部输入的参数来决定要执行的SQL语句时,常常需要动态来构造SQL查询语句,个人觉得用得比较多的地方就是分页存储过程和执行搜索查询的SQL语句。一个比较通用的分页存储过程,可能需要传入表名,字段,过滤条件,排序等参数,而对于搜索的话,可能要根据搜索条件判断来动态执行SQL语句。

    在SQL Server中有两种方式来执行动态SQL语句,分别是exec和sp_executesql。sp_executesql相对而言具有更多的优点,它提供了输入输出接口,可以将输入输出变量直接传递到SQL语句中,而exec只能通过拼接的方式来实现。还有一个优点就是sp_executesql,能够重用执行计划,这就大大提高了执行的性能。所以一般情况下建议选择sp_executesql来执行动态SQL语句。

    使用sp_executesql需要注意的一点就是,它后面执行的SQL语句必须是Unicode编码的字符串,所以在声明存储动态SQL语句的变量时必须声明为nvarchar类型,否则在执行的时候会报“过程需要类型为 'ntext/nchar/nvarchar' 的参数 '@statement'”的错误,如果是使用sp_executesql直接执行SQL语句,则必须在前面加上大写字母N,以表明后面的字符串是使用Unicode类型编码的。

    下面来看看几种动态执行SQL语句的情况

    1.普通SQL语句

    (1)exec('select * from Student')

    (2)exec sp_executesql N'select * from Student'--此处一定要加上N,否则会报错

    2.带参数的SQL语句

    (1)declare @sql nvarchar(1000)

    declare @userId varchar(100)

    set @userId='0001'

    set @sql='select * from Student where UserID='''+@userId+''''

    exec(@sql)

    (2)declare @sql nvarchar(1000)

    declare @userId varchar(100)

    set @userId='0001'

    set @sql=N'select * from Student where UserID=@userId'

    exec sp_executesql @sql,N'@userId varchar(100)',@userId

    从这个例子中可以看出使用sp_executesql可以直接将参数写在sql语句中,而exec需要使用拼接的方式,这在一定程度上可以防止SQL注入,因此sp_executesql拥有更高的安全性。另外需要注意的是,存储sql语句的变量必须声明为nvarchar类型的。

    (3)带输出参数的SQL语句

    create procedure sp_GetNameByUserId

    (

    @userId varchar(100),

    @userName varchar(100) output

    )

    as

    declare @sql nvarchar(1000)

    set @sql=N'select @userName=UserName from Student where UserId=@userId'

    exec sp_executesql N'@userId varchar(100),@userName varchar(100) output',@userId,@userName output

    select @userName

    展开全文
  • 在 Spring Boot 中使用 Dataway 配置数据查询有参接口 Dataway介绍 Dataway 是基于 DataQL 服务聚合能力,为应用提供的一个接口配置工具。使得使用者无需开发任何代码就配置一个满足需求的接口。 整个接口配置、测试...

    使用 Dataway 配置数据查询 与前端参数整合

    Dataway介绍
    Dataway 是基于 DataQL 服务聚合能力,为应用提供的一个接口配置工具。使得使用者无需开发任何代码就配置一个满足需求的接口。 整个接口配置、测试、冒烟、发布。一站式都通过 Dataway 提供的 UI 界面完成。UI 会以 Jar 包方式提供并集成到应用中并和应用共享同一个 http 端口,应用无需单独为 Dataway 开辟新的管理端口。

    这种内嵌集成方式模式的优点是,可以使得大部分老项目都可以在无侵入的情况下直接应用 Dataway。进而改进老项目的迭代效率,大大减少企业项目研发成本。

    Dataway 工具化的提供 DataQL 配置能力。这种研发模式的变革使得,相当多的需求开发场景只需要配置即可完成交付。 从而避免了从数据存取到前端接口之间的一系列开发任务,例如:Mapper、BO、VO、DO、DAO、Service、Controller 统统不在需要。

    Dataway 是 Hasor 生态中的一员,因此在 Spring 中使用 Dataway 首先要做的就是打通两个生态。根据官方文档中推荐的方式我们将 Hasor 和 Spring Boot 整合起来。这里是原文:https://www.hasor.net/web/extends/spring/for_boot.html

    不设置请求头默认的请求 contentType
    默认值: “application/x-www-form-urlencoded”。服务器是接收不到参数的
    并且需要使用JSON.stringify转化成String类型

    // sql模式(mysql)
    -- a new Query.
    select a.* from work as a  inner join user_work as b on a.work_id=b.work_id  where user_id=#{user_id}
    //datasql
    var dataSet = @@sql(work_id) 
    <%
        select * from work   where work_id=#{work_id};
    %>
    return dataSet(${work_id});
    
    //前段请求参数
     let user_id={user_id:1}
        $.ajax({
    		type : "post",
    		contentType:"application/json; charset=UTF-8",
    		dataType: 'json',
    		url : "../../interface/sel_work",
    		data :JSON.stringify(user_id) ,
    		success : function(data) {					 						
    	}});
    
    展开全文
  • Mysql 相关参数配置

    2021-01-20 03:04:07
    一、慢查询日志配置#以下是开启慢日志的配置# 单位为 sslow-query-log = onslow_query_log_file = D:/mysql-8.0.21-winx64/slowlog/slow.loglong_query_time = 0.001log_queries_not_using_indexes = offlong_query_...
  • 我有一个远程SPARQL端点,并且我已经编写了一个简单的查询,该查询可以正常工作,但是现在我需要使用一些参数.到目前为止,这是我的代码:final static String serviceEndpoint = ...
  • 为什么MySQL不推荐使用子查询和join

    千次阅读 2021-01-19 01:06:14
    2.子查询就更别用了,效率太差,执行子查询时,MYSQL需要创建临时表,查询完毕后再删除这些临时表,所以,子查询的速度会受到一定的影响,这里多了一个创建和销毁临时表的过程。3.如果是JOIN的话,它是走嵌套查询...
  • 手把手教你用Java设计并实现一个城市公交查询系统

    千次阅读 多人点赞 2020-12-19 10:11:33
    该系统顺应了时代发展且具有以下优点:首先,方便乘客的出行,乘客不用询问站牌工作人员如何倒车,便可到达目的地。其次,乘客在网上就可以查询到公交公司发布的路况信息,以便提早做好换乘准备节约出..
  • 经过重新编译之后存储在数据库中,再通过指定的名字,并且提供参数来执行它。一、SQL Server 存储过程Transact-SQL非常类似于Java语言中的方法,它可以重复调用。当存储过程执行一次后,可以将语句缓存中,这样下次...
  • 【单选题】带有默认值的参数一定位于参数列表的末尾。( )【单选题】Python源代码被解释器转换后的格式为( )。【单选题】Python可以开发Web程序,也可以管理操作系统。( ) x = 'abc' y = x y = 100 print(x)【单选题】...
  • 1,调用没有参数的存储过程set conn=server.CreateObject("adodb.connection")set cmd=server.CreateObject("adodb.command")strconn="dsn=pubs;uid=sa;pwd"conn.Open strconnset cmd.ActiveConnection=conncmd....
  • 一、内容总览1.1、函数参数和函数返回值的作用1.2、函数的返回值进阶1.3、函数的参数进阶1.4、递归函数二、函数参数和函数返回值的作用2.1、函数根据有没有参数以及有没有返回值,相互组合之后有以下四种情况无参数 ...
  • Servlet URL重写带参数

    2021-02-26 16:05:20
    在URL重写中,我们将令牌或标识符附加到下一个Servlet或下一个资源的URL。可以使用以下格式发送...符号将参数名/值对与其他参数分开。当用户单击超链接时,参数名称/值对将被传递到服务器。 从Servlet中可以使用ge...
  • 使用Filter有一个优点:不需要理解SpringMVC的流程,也不需要扩展SpringMVC的相关组件。缺点也比较明显: 1、如果需要区分加解密,只能通过URL规则进行过滤。 2、需要加密的接口的SpringMVC控制器的返回参数必须是...
  • vue.js有什么优点

    2021-07-06 04:06:50
    Vue.js通过组件,把一个单页应用中的各种模块拆分到一个一个单独的组件(component)中,我们只要先在父级应用中写好各种组件标签(占坑),并且在组件标签中写好要传入组件的参数(就像给函数传入参数一样,这个...
  • 使用Filter有一个优点:不需要理解SpringMVC的流程,也不需要扩展SpringMVC的相关组件。缺点也比较明显: 1、如果需要区分加解密,只能通过URL规则进行过滤。 2、需要加密的接口的SpringMVC控制器的返回参数必须是...
  • 查询所有的pid并杀死。jps -l | grep bdcsc2-native-demo | awk '{print $1}' | xargs kill -9KISS:keep it short and simple!1. 符号:` `名称:反引号,上分隔符位置:反引号(`)这个字符一般在键盘的左上角,数字1...
  • “KM”的答案就其本身而言是好的,但未能完全贯彻他早期的一条建议;...在实践中,可能有一组通用的参数组合,这些组合可以通过定制查询进行目标,然后是针对所有其他组合的通用查询(与其他答案一样)。CRE...
  • 存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。优点1. ...
  • MySQL查询某些日期范围

    千次阅读 2021-01-25 22:13:41
    因此,另一种解决方案很简单: select*from table where end_date >= start_search && start_date 此解决方案的优点是我们只需要进行2次比较.与“OR OR”方法形成对比,该方法需要2到8(3& plus; 3& plus; 2)比较. ...
  • 11-23 diaba 技术框架通过如下代码设置参数是否必输:@RequestParam(value = "parameterName", required = true) String parameterName优点:简单,不用代码进行检查输入参数是否有值缺点:如果没有填写该参数值,被...
  • 概念:存储过程是一个预编译的SQL语句,优点是允许模块化的设计,就是说只需创建一次,以后在程序中就可以调用多次。 如何调用: 可以用一个“execute 存储过程名 参数”命令来调用存储过程。 优点及其缺点: 优势:...
  • 需求:后台管理系统列表,带查询条件和分页,点编辑,新页面打开,保存之后再跳转回之前的页面。(如果是表单字段少,强烈建议dialog修改。请忽略本文)实现思路:store存储一个map,这个map的键是列表页面的path(也...
  • [转载]MRT投影参数设置及原理

    千次阅读 2021-02-11 22:34:06
    参数设置2.误区3.原理4.如何选择投影和基准面【一】投影转换的设置·中国地区Albers投影参数的设置坐标系:大地坐标系投 影:Albers正轴等面积双标准纬线圆锥投影南标准纬线:25°N北标准纬线:47°N中央经线:105°...
  • 一、关于datasheet与查询datasheet 1、datasheet是什么 2、如何查询元件的datasheet? 二、免费datasheet查询网站 1、AllDatasheet(简体中文) 2、半导小芯——专门提供芯片资料查询的免费工具 一、关于data...
  • MySQL查询性能优化(精)

    2021-01-27 19:44:03
    MySQL查询性能优化MySQL查询性能的优化涉及多个方面,其中包括库表结构、建立合理的索引、设计合理的查询。库表结构包括如何设计表之间的关联、表字段的数据类型等。这需要依据具体的场景进行设计。如下我们从数据库...
  • JVM参数详解及Arthas使用

    千次阅读 2021-12-14 17:14:33
    介绍JVM垃圾回收制度、垃圾回收算法、垃圾收集器、JVM参数、arthas使用及相关问题实战。
  • MySQL Query Cache(MySQL查询缓存)在MySQL Server中是默认打开的,但是网上各种资料以及有经验的DBA都建议生产环境中把MySQL Query Cache关闭。按道理,MySQL Server默认打开,是鼓励用户使用缓存,但是大拿们却建议...
  • 几乎每个完整的应用程序都会需要一个复合查询。建立一个功能强大的复合查询首先必须要能够动态生成查询条件,其次...三是建立一个视图,其中包括参数化视图、宏替换Sql语句视图;四是建立一个Grid,将其数据源设置为...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 191,586
精华内容 76,634
关键字:

参数查询优点