SQL SERVER优化


本文主要讲解如何使用alter indexrebuildreorganize索引来清除碎片,rebuild能够完全清除碎片,但是reorganize却不能。

Rebuild index

--1.准备实验数据
select * into Employee from AdventureWorks2008R2.HumanResources.Employee;

--2.查看使用空间:Employee    290            72 KB    56 KB    8 KB    8 KB
sp_spaceused Employee

--3.创建聚集索引
create clustered index IX_BusinessEntityID on Employee(BusinessEntityID);

--4.查看使用空间:Employee    290            80 KB    56 KB    16 KB    8 KB
sp_spaceused Employee

--5.索引重建,清除fragment,并设定fillfactor为60
ALTER INDEX ALL ON Employee
REBUILD WITH (FILLFACTOR = 60, SORT_IN_TEMPDB = ON,
              STATISTICS_NORECOMPUTE = ON);

--6.查看使用空间:Employee    290            144 KB    88 KB    16 KB    40 KB
sp_spaceused Employee

结论

1.在创建索引以后,index从8变到16,说明索引占用物理磁盘,因此索引是一个physical object
2.在重建索引并设定fillfactor60以后,我们发现data空间变大,这是因为填充因子重新使得原来装满的data page现在只装60%
3.fillfactor只在对已有数据create indexalter index rebuild的时候有用。对于普通的insert操作无效。
4.fillfactor的取值是1-100,msnd上说取0跟取100一样,但是实际测试发现使用0报错。

reorganize index

ALTER INDEX ALL ON Employee
REORGANIZE
GO

两者的区别

Rebuilding an index drops and re-creates the index. This removes fragmentation, reclaims disk space by compacting the pages based on the specified or existing fill factor setting, and reorders the index rows in contiguous pages. When ALL is specified, all indexes on the table are dropped and rebuilt in a single transaction.

重新生成索引将会删除并重新创建索引。 这将根据指定的或现有的填充因子设置压缩页来删除碎片、回收磁盘空间,然后对连续页中的索引行重新排序。 如果指定 ALL,将删除表中的所有索引,然后在单个事务中重新生成。

Reorganizing an index uses minimal system resources. It defragments the leaf level of clustered and nonclustered indexes on tables and views by physically reordering the leaf-level pages to match the logical, left to right, order of the leaf nodes. Reorganizing also compacts the index pages. Compaction is based on the existing fill factor value.

重新组织索引使用最少系统资源重新组织索引。 通过对叶级页以物理方式重新排序,使之与叶节点的从左到右的逻辑顺序相匹配,进而对表和视图中的聚集索引和非聚集索引的叶级进行碎片整理。 重新组织还会压缩索引页。 压缩基于现有的填充因子值。

Rebuilding an index can be executed online or offline. Reorganizing an index is always executed online. To achieve availability similar to the reorganize option, you should rebuild indexes online.

rebulid index既可以在online又可以在offline下执行,而reorganize index只能在online下执行的。

Difference between rebuild index online and offline(PS:2012-9-11)

既然rebuild index既可以是online模式,也可以是offline模式,那么两者有什么区别呢。这个我们可以参考stackoverflow上面的一篇文章:What is the difference between OFFLINE and ONLINE index rebuild in SQL Server? 在这里我还是简要总结一下:

online模式下

rebuild index会复制旧索引来新建索引,此时旧的索引依然可以被读取和修改,但是所以在旧索引上的修改都会同步更新到新索引下。中间会有一些冲突解决机制,具体参考Online Index Operations 里面的Build Phase这一章节。然后在rebuild这个过程完整的时候,会对table上锁一段时间,在这段时间里会用新索引来替换旧索引,当这个过程完成以后再释放table上面的锁。如果索引列包含 LOB对象的话,在SQL Server 2005/2008/R2rebuild index online会失败。在sql server 2012中,即使索引列包含LOB对象,也可以rebuild index online了,可以参考 Online Index Operations for indexes containing LOB columns.

offline模式下

rebuilde index会对table上锁,所有对这个table的读写操作都会被阻塞,在这期间新索引根据旧索引来创建,其实就是一个复制的过程,但是新索引没有碎片,最后使用新索引替换旧索引。当rebuild整个过程完成以后,table上面的锁才会被释放。


三、实际运用

在官方文档中涉及到二十几列,但是实际上用不着关心那么多列,只需要查看几列的信息就足以了。

SELECT   
    dbInfo.name AS databaseName,  
    objInfo.name AS objName,  
    ixInfo.name AS ixName,  
    index_stats.avg_fragmentation_in_percent,  
    index_stats.fragment_count,  
    index_stats.page_count,  
    index_stats.avg_page_space_used_in_percent,  
    index_stats.record_count,  
    index_stats.avg_record_size_in_bytes  
FROM sys.dm_db_index_physical_stats(NULL,NULL,NULL,NULL,DEFAULT) AS index_stats  
INNER JOIN sys.databases AS dbInfo ON index_stats.database_id = dbInfo.database_id  
INNER JOIN sys.objects AS objInfo ON objInfo.object_id=index_stats.object_id  
INNER JOIN sys.indexes AS ixInfo ON index_stats.index_id = ixInfo.index_id  
WHERE 1=1  
AND dbInfo.name=N'test'  
AND objInfo.name='TB_TEST'  
AND ixInfo.name='IX_PK_TB_TEST'  

(1)、avg_fragmentation_in_percent
表示索引与堆的逻辑平均碎片百分比,
如果该值为10%-20%,碎片应该没有什么大问题;
如果该值为20%-40%,碎片有可能成为问题,可以通过索引重组来完成
如果该值为大于40%,可能要求索引重建
(2)、fragment_count
表示碎片数量,或者是组成索引单独页面的数量
(3)、page_count
组成统计的索引或数据页面数量的统计

四、碎片处理

卸载并且重建索引

使用DROP_EXISTING子句重建索引

执行ALTER INDEX REBUILD语句

执行ALTER INDEX REORGANIZE

(1)、卸载并且重建索引

该方式是最简单 的方法,就是先删除在创建,但是该方式也有许多缺点

1、阻塞,该方式会造成系统大的开销,并且可能导致阻塞,因为如果数据过大,该操作是很耗时间的,在该期间可能阻塞该表上的其他操作

2、丢失索引,因为在索引卸载等待重建期间,该表的查询没有索引可以使用,导致查询性能下降

3、非聚簇索引,因为卸载重建的索引是聚簇索引,那么其他的非聚簇索引也必须被重建,因为非聚簇索引是建立在聚簇索引之上的

4、唯一性约束,用于定义主键或者唯一性约束的索引不能被删除。并且主键等可能被外键引用,在卸载主键之前必须先卸载外键

因此,该方式只适合在没有什么人访问数据库的的空闲时候采用,有足够的时间慢慢倒腾

(2)、使用DROP_EXISTING子句重建索引

在重建聚簇索引的时候,使用DROP_EXISTING子句可以避免重建非聚簇索引,因为行定位器使用的索引键值保持不变,并且是在同一原子步骤中。
可以为聚簇索引与非聚簇索引使用DROP_EXISTING子句,甚至可以将非聚簇索引转换为聚簇索引,但是不能将聚簇索引转换为非聚簇索引。
当然该方式也是有缺点的

1、阻塞,与卸载重建方式类似,当资源紧张时也可能导致阻塞

2、使用约束的索引,该方式可以重新创建具有约束的索引,但是如果该约束是主键或是与外键相关的唯一性约束,有可能会有问题

3、具有多个碎片化的索引的表,由于数据产生碎片,索引常常也随之产生碎片,如果使用该碎片技术,表上所有索引必须单独重建

(3)、使用ALTER INDEX REBUILD语句

该操作是在一个原子步骤中完成的,是物理的重建索引。但是并不是先删除在创建而是动态的重建索引,将碎片降低到最低程度。
尽管该方式是很常用的索引重建方式,但是仍有一些缺陷:

1、阻塞,与前两种方式类似,当资源紧张时也是有可能造成堵塞的

2、事务回滚,因为该操作是一个原子操作,如果该操作中途停止,事务将会回滚,那么之前碎片整理操作将会丢失

(4)、使用ALTER INDEX REORGANIZE语句

该方式并不是物理重建,而是采用逻辑方式按照索引键的逻辑顺序重新排列现有的索引叶子页面来减少碎片。
该方式是非原子性的操作
当然该方式也是有缺点的:

1、该方式不能像REBUILD方式有效的减少碎片

2、对于高度碎片化的索引,该方式比=重建索引更耗时间

3、如果索引跨越多个文件,该方式不能在文件之间移动页面

(5)、集中方式的特性比较

特性/问题卸载并且重建使用DROP_EXISTINGREBUILDREORGANIZE
在整理聚簇索引碎片时
重建非聚簇索引
两次
丢失索引
整理具有约束的索引碎片很复杂一般复杂简单简单
同时进行多个索引碎片整理
并发性一般
中途撤销因为不使用事务而存在危险进程丢失进程丢失进程被保留
碎片整理程度中一般
应用新的填充因子
更新统计

内部碎片

当索引页没有用到最大量时就产生了内部碎片。虽然在一个有频繁数据插入的应用程序里这也许有帮助,然而设置一个fill factor(填充因子)会在索引页上留下空间,服务器内部碎片会导致索引尺寸增加,从而在返回需要的数据时要执行额外的读操作。这些额外的读操作会降低查询的性能。

怎样确定索引是否有碎片?

SQL Server提供了一个数据库命令――DBCC SHOWCONTIG――来确定一个指定的表或索引是否有碎片。

DBCC SHOWCONTIG

数据库平台命令,用来显示指定的表的数据和索引的碎片信息。

DBCC SHOWCONTIG 权限默认授予 sysadmin固定服务器角色或 db_ownerdb_ddladmin固定数据库角色的成员以及表的所有者且不可转让。

语法(SQLServer2000)

DBCC SHOWCONTIG 
[ ( { table_name | table_id| view_name | view_id } 
[ , index_name | index_id ] 
) 
] 
[ WITH { ALL_INDEXES 
| FAST [ , ALL_INDEXES ] 
| TABLERESULTS [ , { ALL_INDEXES } ] 
[ , { FAST | ALL_LEVELS } ] 
}
]

语法(SQLServer7.0)

DBCC SHOWCONTIG 
[ ( table_id [,index_id ] 
) 
]

示例:
显示数据库里所有索引的碎片信息

SET NOCOUNT ON
USE pubs
DBCC SHOWCONTIG WITH ALL_INDEXES

GO

显示指定表的所有索引的碎片信息

SET NOCOUNT ONUSE pubs
DBCC SHOWCONTIG (authors) WITH ALL_INDEXES

GO

显示指定索引的碎片信息

SET NOCOUNT ON
USE pubs
DBCC SHOWCONTIG (authors,aunmind)

GO

结果集

DBCC SHOWCONTIG将返回扫描页数、扫描扩展盘区数、遍历索引或表的页时,DBCC 语句从一个扩展盘区移动到其它扩展盘区的次数、每个扩展盘区的页数、扫描密度(最佳值是指在一切都连续地链接的情况下,扩展盘区更改的理想数目)。

DBCC SHOWCONTIG 正在扫描 'authors'...
表: 'authors'1977058079);
索引 ID: 1,数据库 ID: 5
已执行 TABLE 级别的扫描。
- 扫描页数.....................................: 1
- 扫描扩展盘区数...............................: 1
- 扩展盘区开关数...............................: 0
- 每个扩展盘区上的平均页数.....................: 1.0
- 扫描密度[最佳值:实际值]....................: 100.00%1:1- 逻辑扫描碎片.................................: 0.00%
- 扩展盘区扫描碎片.............................: 0.00%
- 每页上的平均可用字节数.......................: 6010.0
- 平均页密度(完整)...........................: 25.75%

DBCC 执行完毕。如果DBCC 输出了错误信息,请与系统管理员联系。

寻找什么

扫描页数:如果你知道行的近似尺寸和表或索引里的行数,那么你可以估计出索引里的页数。看看扫描页数,如果明显比你估计的页数要高,说明存在内部碎片。

扫描扩展盘区数:用扫描页数除以8,四舍五入到下一个最高值。该值应该和DBCC SHOWCONTIG返回的扫描扩展盘区数一致。如果DBCC SHOWCONTIG返回的数高,说明存在外部碎片。碎片的严重程度依赖于刚才显示的值比估计值高多少。

扩展盘区开关数:该数应该等于扫描扩展盘区数减1。高了则说明有外部碎片。

每个扩展盘区上的平均页数:该数是扫描页数除以扫描扩展盘区数,一般是8。小于8说明有外部碎片。

扫描密度[最佳值:实际值]:DBCC SHOWCONTIG返回最有用的一个百分比。这是扩展盘区的最佳值和实际值的比率。该百分比应该尽可能靠近100%。低了则说明有外部碎片。

逻辑扫描碎片:无序页的百分比。该百分比应该在0%10%之间,高了则说明有外部碎片。

扩展盘区扫描碎片:无序扩展盘区在扫描索引叶级页中所占的百分比。该百分比应该是0%,高了则说明有外部碎片。

每页上的平均可用字节数:所扫描的页上的平均可用字节数。越高说明有内部碎片,不过在你用这个数字决定是否有内部碎片之前,应该考虑fill factor(填充因子)。

平均页密度(完整):每页上的平均可用字节数的百分比的相反数。低的百分比说明有内部碎片。

备注

DBCC SHOWCONTIG实际上仅对那些大表有用。小表显示的结果根本不符合正常标准,因为他们也许没有由多于8个的页面组成。你在查看小表上执行DBCC SHOWCONTIG的结果时应该忽略一些结果。在处理小表时只需关心扩展盘区开关数、逻辑扫描碎片、每页上的平均可用字节数、平均页密度(完整)。

DBCC SHOWCONTIG默认输出的结果是:扫描页数、扫描扩展盘区数、扩展盘区开关数、每个扩展盘区上的平均页数、扫描密度[最佳值:实际值]、逻辑扫描碎片、扩展盘区扫描碎片、每页上的平均可用字节数、平均页密度(完整)。可以用FASTTABLERESULTS选项来控制这个输出结果。

FAST选项指定执行索引的快速扫描,输出结果是最小的,该选项不读索引的叶或数据页且只返回扫描页数、扫描扩展盘区数、扫描密度[最佳值:实际值]、逻辑扫描碎片。

TABLERESULTS选项将用行集的形式显示信息,将返回扩展盘区开关数、扫描密度[最佳值:实际值]、逻辑扫描碎片、扩展盘区扫描碎片、每页上的平均可用字节数、平均页密度(完整)。

如果既指定FAST选项又指定TABLERESULTS选项,那么将返回对象名、对象ID、索引名、索引ID,页数、扩展盘区开关数、扫描密度[最佳值:实际值]和逻辑扫描碎片。

ALL_INDEXES选项将显示指定表和试图的所有索引的结果,即使指定了一个索引。

ALL_LEVELS选项指定是否为所处理的每个索引的每个级别产生输出(默认只输出索引的页级或表数据级的结果),并且只能与 TABLERESULTS 选项一起使用。

解决碎片问题

一旦你确定表或索引有碎片问题,那么你有4个选择去解决那些问题:

  • 删除并重建索引
  • 使用DROP_EXISTING子句重建索引
  • 执行DBCC DBREINDEX
  • 执行DBCC INDEXDEFRAG

尽管每一个技术都能达到你整理索引碎片的最终目的,但各有各的优缺点。

删除并重建索引

DROP INDEXCREATE INDEXALTER TABLE来 删除并重建索引有些缺陷包括在删除重建期间索引会消失。在索引删除重建时,对于查询它不在可用,查询性能也许会受到明显的影响,直到重建索引为止。另一个 潜在的缺陷是当都请求索引的时候会引起阻塞,直到重建索引为止。通过其他的处理也能解决阻塞,就是索引被使用的时候不删除索引。另一个主要的缺陷是在用DROP INDEXCREATE INDEX重建聚集索引时会引起非聚集索引重建两次。删除聚集索引时非聚集索引的行指针会指向数据堆,聚集索引重建时非聚集索引的行指针又会指回聚集索引的行位置。

删除并重建索引的确有一个好处就是通过重新排序索引页,使索引页紧凑并删除不需要的索引页来完全重建索引。你也许需要考虑那些内部和外部碎片都很高的情况下才使用,以使那些索引回到它们应该在的位置。

使用DROP_EXISTING子句重建索引

为了避免在重建聚集索引时表上的非聚集索引重建两次,可以使用带DROP_EXISTING子句的CREATE INDEX语句。这个子句会保留聚集索引键值,以避免非聚集索引重建两次。和删除并重建索引一样,该方法也可能会引起阻塞和索引消失的问题。该方法的另一个缺陷是也强迫你去分别发现和修复表上的每一个索引。

除了和上一个方法一样的好处之外,该方法的好处是不必重建非聚集索引两次。这样可以对那些带约束的索引提供正确的索引定义以符合约束的要求。

执行DBCC DBREINDEX

DBCC DBREINDEX类似于第二种方法,但它物理地重建索引,允许SQLServer给索引分配新页来减少内部和外部碎片。DBCC DBREINDEX也能动态的重建带约束的索引,不象第二种方法。

DBCC DBREINDEX的缺陷是会遇到或引起阻塞问题。DBCC DBREINDEX是作为一个事务来运行的,所以如果在完成之前中断了,那么你会丢失所有已经执行过的碎片。

执行DBCC INDEXDEFRAG

DBCC INDEXDEFRAG(在SQLServer2000中可用)按照索引键的逻辑顺序,通过重新整理索引里存在的叶页来减少外部碎片,通过压缩索引页里的行然后删除那些由此产生的不需要的页来减少内部碎片。它不会遇到阻塞问题但它的结果没有其他几个方法彻底。这是因为DBCC INDEXDEFRAG跳过了锁定的页且不使用任何新页来重新排序索引。如果索引的碎片数量大的话你也许会发现DBCC INDEXDEFRAG比重建索引花费的时间更长。DBCC INDEXDEFRAG比其他方法的确有好处的是在其他过程访问索引时也能进行碎片整理,不会引起其他方法的阻塞问题。


总结

如何在SQLServer中处理亿万级别的数据(历史数据),可以按以下方面进行:

  1. 去掉表的所有索引
  2. 用BulkCopy进行插入
  3. 分表或者分区,减少每个表的数据总量
  4. 在某个表完全写完之后再建立索引
  5. 正确的指定索引字段
  6. 把需要用到的字段放到包含索引中(在返回的索引中就包含了一切)

SQL Server提供了一些函数返回连接值(这里可不是当前连接数哟!),个人觉得,很容易产生误解.

系统变量

@@CONNECTIONS 返回自上次启动 Microsoft SQL Server 以来连接或试图连接的次数。

@@MAX_CONNECTIONS 返回 Microsoft SQL Server 上允许的同时用户连接的最大数。返回的数不必为当前配置的数值。

系统存储过程

SP_WHO

提供关于当前 Microsoft SQL Server 用户和进程的信息。可以筛选返回的信息,以便只返回那些不是空闲的进程。

列出所有活动的用户:SP_WHO ‘active’

列出某个特定用户的信息:SP_WHO ‘sa’

系统表

Sysprocesses

sysprocesses 表中保存关于运行在 Microsoft SQL Server 上的进程的信息。这些进程可以是客户端进程或系统进程。sysprocesses 只存储在 master 数据库中。

Sysperfinfo

包括一个 Microsoft SQL Server 表示法的内部性能计数器,可通过 Windows NT 性能监视器显示.

有人提议说为了获取SQL Server的当前连接数:使用如下SQL:

SELECT COUNT(*) AS CONNECTIONS FROM master..sysprocesses

个人认为不对,看看.sysprocesseslogin_time列就可看出.

另外一个方面是进程不能和连接相提并论,他们是一对一的关系吗,也就是说一个进程就是一个连接?一个连接应该有多个进程的,所以连接和进程之间的关系应该是1:n的.

因为sysprocesses列出的进程包含了系统进程和用户进程,为了得到用户连接,可以使用如下SQL:

SELECT cntr_value AS User_Connections FROM master..sysperfinfo as p
WHERE p.object_name = 'SQLServer:General Statistics' And p.counter_name = 'User Connections'

个人还是认为不对,因为它是一个计数器,可能会累加的.

还有一种方案是利用如下SQL:

select connectnum=count(distinct net_address)-1 from master..sysprocesses

理由是net_address是访问者机器的网卡值,这个总该是唯一的吧.但是看起来得到的是所有时间内的连接数.


SQL语句设置最大连接数

下面的 T-SQL 语句可以配置 SQL Server 允许的并发用户连接的最大数目。

exec sp_configure ‘show advanced options’, 1
exec sp_configure ‘user connections’, 100

第一句用以表示显示 sp_configure 系统存储过程高级选项,使用 user connections 时,要求 show advanced options 值为 1。

第二句配置最大连接数为 100,0 表示不限制,但不表示无限,后面将谈谈。

也可以在企业管理器中配置,在企业管理器中,可以在实例上点右键->“属性”->“连接”里面更改。

需要重新启动 SQL Server,该值才会生效。


@@max_connections

select @@max_connections

它总是返回 32767,它并不是指上面设置的 user connections,实际上它表示 user connections 最大可设置为多少。由于它的最大值是 32767,那么 user connections0 时,最大连接数也就是 32767 了,并不是无限。

默认情况下 user connections 值是 0,也就是说默认情况下 SQL Server 的最大连接数是 32767


阿里云RDS数据库 创建索引 来优化

CREATE INDEX IDX_STATUS ON [xxx].[dbo].[Orders](STATUS) WITH(ONLINE=ON)

CREATE INDEX IDX_NU ON [xxx].[dbo].[DeliveryStatus](NU) WITH(ONLINE=ON)

CREATE INDEX IDX_DID_LDTIME ON [xxx].[dbo].[LD](DID,LDTIME) WITH(ONLINE=ON)
CREATE INDEX IDX_NU ON [xxx].[dbo].[DeliveryStatus](NU) WITH(ONLINE=ON)

CREATE INDEX IDX_SKEY ON [XXX].[dbo].[SysManager](SKEY) WITH(ONLINE=ON)

CREATE INDEX IDX_STATUS_ANUM ON [XXX].[dbo].[Orders](STATUS,ANUM) WITH(ONLINE=ON)
CREATE INDEX IDX_ANUM ON [XXX].[dbo].[Inventory](ANUM) WITH(ONLINE=ON)

CREATE INDEX IDX_DI_LD_CO ON [XXX].[dbo].[LD](DID,LDTIME) INCLUDE (CONTEXT) WITH(ONLINE=ON)

CREATE INDEX IDX_MID ON [XXX].[dbo].[Orders](MID) WITH(ONLINE=ON)

CREATE INDEX IDX_MID_ORDERSDT ON [XXX].[dbo].[Orders](MID,ORDERSDT) WITH(ONLINE=ON)

CREATE INDEX IDX_AREAKEY ON [xxx].[dbo].[AreaC](AREAKEY) WITH(ONLINE=ON)

CREATE INDEX IDX_ANUM_MID ON [XXX].[dbo].[Orders](ANUM,MID) WITH(ONLINE=ON)

CREATE INDEX IDX_ANUMCSTOCK ON [XXX].[dbo].[Inventory](ANUM,CURRENTSTOCK) WITH(ONLINE=ON)

CREATE INDEX IDX_ICCO ON [XXX].[dbo].[Coupon](IS_DELETE,IS_USED,GROUPID,COUPONCODE,COUPONID) WITH(ONLINE=ON)

CREATE INDEX IDX_ONUM ON [XXX].[dbo].[Coupon](ONUM) WITH(ONLINE=ON)

CREATE INDEX IDX_ANUM ON [XXX].[dbo].[Specification](ANUM) WITH(ONLINE=ON)

CREATE INDEX IDX_NCP ON [XXX].[dbo].[AccessLog](ANUM,ACCESSDT) INCLUDE (OPENID) WITH(ONLINE=ON)

existsin的区别和性能比较

inexists
in 是把外表和内表作hash 连接,而exists是对外表作loop循环,每次loop循环再对内表进行查询。
一直以来认为existsin效率高的说法是不准确的。
如果查询的两个表大小相当,那么用inexists差别不大。
如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in

例如:表A(小表),表B(大表)
1:

select * from A where cc in (select cc from B)

效率低,用到了A表上cc列的索引;

select * from A where exists(select cc from B where cc=A.cc)

效率高,用到了B表上cc列的索引。
相反的
2:

select * from B where cc in (select cc from A)

效率高,用到了B表上cc列的索引;

select * from B where exists (select cc from A where cc=B.cc)

效率低,用到了A表上cc列的索引。

not innot exists

如果查询语句使用了not in 那么内外表都进行全表扫描,没有用到索引;而not extsts 的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。

in= 的区别

select name from student where name in ('zhang','wang','li','zhao');

select name from student where name ='zhang' or name='li' or name='wang' or name='zhao'

的结果是相同的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值