本周技术研究部(TRD)的一名DBA 对我们编写SQL时的一些问题,进行了汇报讲演,以下是来自它的脚本,我在它讲演的基础上写出了自己想表述的,以便于大家相互交流学习
代码如下:/*
--注意:准备数据(可略过,非常耗时)
CREATE TABLE CHECK1_T1
(
ID INT,
C1 CHAR(8000)
)
CREATE TABLE CHECK1_T2
(
ID INT,
C1 CHAR(8000)
)
DECLARE @I INT
SET @I=1
WHILE @IBEGIN
INSERT INTO CHECK1_T1 SELECT @I,'C1'
INSERT INTO CHECK1_T2 SELECT 10000+@I,'C1'
SET @I=@I+1
END
CREATE TABLE CHECK2_T1
(
ID INT,
C1 CHAR(8000)
)
DECLARE @I INT
SET @I=1
WHILE @IBEGIN
INSERT INTO CHECK2_T1 SELECT @I,'C1'
SET @I=@I+1
END
INSERT INTO CHECK2_T1 VALUES(10001,'C2')
INSERT INTO CHECK2_T1 VALUES(10002,'C1')
CREATE TABLE CHECK3_T1
(
ID INT,
C1 CHAR(7000)
)
CREATE TABLE CHECK3_T2
(
ID INT,
C1 CHAR(7000)
)
DECLARE @I INT
SET @I=1
WHILE @IBEGIN
IF @I%2 =0
BEGIN
INSERT INTO CHECK3_T1 SELECT @I,'C1'
END
ELSE
BEGIN
INSERT INTO CHECK3_T1 SELECT @I,'C2'
END
IF @I%100=0
BEGIN
INSERT INTO CHECK3_T2 SELECT @I,'C1'
INSERT INTO CHECK3_T2 SELECT @I+50000,'C2'
END
SET @I=@I+1
END
CREATE TABLE CHECK4_T1
(
ID INT,
C1 CHAR(500),
)
DECLARE @I INT
SET @I=1
WHILE @IBEGIN
IF @I%100000 =0
BEGIN
INSERT INTO CHECK4_T1 SELECT @I,'C2'
END
ELSE
BEGIN
INSERT INTO CHECK4_T1 SELECT @I,'C1'
END
SET @I=@I+1
END
CREATE NONCLUSTERED INDEX NCIX_C1 ON CHECK4_T1(C1)
CREATE TABLE CHECK5_T1
(
ID INT,
C1 CHAR(10),
)
DECLARE @I INT
SET @I=1
WHILE @IBEGIN
INSERT INTO CHECK5_T1 SELECT @I,'C1'
IF @I%2=0
BEGIN
INSERT INTO CHECK5_T1 SELECT @I,'C1'
END
SET @I=@I+1
END
*/
--=====================================
--1、 Union all 代替 Union
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE
--测试一:(26s) 执行计划:表扫描->排序->合并联接
SELECT ID,C1 FROM CHECK1_T1 --1W条数据
UNION
SELECT ID,C1 FROM CHECK1_T2 --1W条数据
--测试二: (4s) 执行计划:表扫描->表扫描串联
SELECT ID,C1 FROM CHECK1_T1 --1W条数据
UNION ALL
SELECT ID,C1 FROM CHECK1_T2 --1W条数据
--总结:测试一中的union 排序和去重合并是相当耗时的,如果不要此功能,大数据时最好加上ALL
--=====================================
--2、 Exists 代替 Count(*)
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE
----测试一: (7s) 执行计划:表扫描-> 流聚合-> 计算矢量
DECLARE @COUNT INT
SELECT @COUNT=COUNT(*) FROM CHECK2_T1 WHERE C1='C1' --1W条数据
IF @COUNT>0
BEGIN
PRINT 'S'
END
----测试二: (0s) 执行计划:常量扫描/表扫描-> 嵌套循环-> 计算标量
IF EXISTS(SELECT 1 FROM CHECK2_T1 WHERE C1='C1') --1W条数据
BEGIN
PRINT 'S'
END
--总结:判断是否存在,用Exist即可,没必要用COUNT(*)将表的所有记录统计出来,扫描一次
--=====================================
--3、 IN(Select COL1 From Table)的代替方式
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE
--测试一: (3s)执行计划:表扫描 -> 哈希匹配
SELECT ID,C1 FROM CHECK3_T2 --400行
WHERE ID IN (SELECT ID FROM CHECK3_T1 WHERE C1='C1') --2W行
--测试二:(1s)执行计划:表扫描-> 并行度 -> 位图 -> 排序 -> 合并联接 -> 并行度
SELECT A.ID,A.C1 FROM CHECK3_T2 A
INNER JOIN CHECK3_T1 B ON A.ID=B.ID WHERE B.C1='C1'
--测试三:(3s)执行计划:表扫描-> 哈希匹配
SELECT A.ID,A.C1 FROM CHECK3_T2 A
WHERE EXISTS (SELECT 1 FROM CHECK3_T1 B WHERE B.ID=A.ID AND B.C1='C1')
--总结:能用INNER JOIN 尽量用它,SQL SERVER在查询时会将关联表进行优化
--=====================================
--4、 Not Exists 代替 Not In
--测试一:(8s) 执行计划:表扫描-> 嵌套循环 -> 哈希匹配
SELECT ID,C1 FROM CHECK3_T1 --2W行
WHERE ID NOT IN (SELECT ID FROM CHECK3_T2 WHERE C1='C1') --400行
--测试二:(4s) 执行计划:表扫描-> 哈希匹配
SELECT A.ID,A.C1 FROM CHECK3_T1 A
WHERE NOT EXISTS (SELECT 1 FROM CHECK3_T2 B WHERE B.ID=A.ID AND B.C1='C1')
--总结:尽量不使用NOT IN ,因为会调用嵌套循环,建议使用NOT EXISTS代替NOT IN
--=====================================
--5、 避免在条件列上使用任何函数
DROP TABLE CHECK4_T1
CREATE NONCLUSTERED INDEX NCIX_C1 ON CHECK4_T1(C1) --加上非聚集索引
---测试一:(4s)执行计划: 索引扫描
SELECT * FROM CHECK4_T1 WHERE RTRIM(C1)='C2'
---测试二:(0s)执行计划: 索引查找
SELECT * FROM CHECK4_T1 WHERE C1='C2'
--总结:where条件里对索引字段使用了函数,会使索引查找变成索引扫描,从而查询效率大幅下降
--=====================================
--6、 用sp_executesql执行动态sql
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE
CREATE PROC UP_CHECK5_T1 (
@ID INT
)
AS
SET NOCOUNT ON
DECLARE @count INT,
@sql NVARCHAR(4000)
SET @sql = 'SELECT @count=count(*) FROM CHECK5_T1 WHERE ID = @ID'
EXEC sp_executesql @sql,
N'@count INT OUTPUT, @ID int',
@count OUTPUT,
@ID
PRINT @count
CREATE PROC UP_CHECK5_T2 (
@ID INT
)
AS
SET NOCOUNT ON
DECLARE @sql NVARCHAR(4000)
SET @sql = 'DECLARE @count INT;SELECT @count=count(*) FROM CHECK5_T1 WHERE ID = ' + CAST(@ID AS VARCHAR(10)) + ';PRINT @count'
EXEC(@sql)
---测试一:瞬时
DECLARE @N INT
SET @N=1
WHILE @NBEGIN
EXEC UP_CHECK5_T1 @N
SET @N=@N+1
END
---测试二:2s
DECLARE @N INT
SET @N=1
WHILE @NBEGIN
EXEC UP_CHECK5_T2 @N
SET @N=@N+1
END
CREATE CLUSTERED INDEX CIX_ID ON CHECK5_T1(ID)
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE
--查看缓存计划
SELECT a.size_in_bytes '占用字节数',
total_elapsed_time / execution_count '平均时间',
total_logical_reads / execution_count '逻辑读',
usecounts '重用次数',
SUBSTRING(d.text, (statement_start_offset / 2) + 1, ((CASE statement_end_offset
WHEN -1 THEN DATALENGTH(text)
ELSE statement_end_offset
END - statement_start_offset) / 2) + 1) '语句'
FROM sys.dm_exec_cached_plans a
CROSS apply sys.dm_exec_query_plan(a.plan_handle) c,
sys.dm_exec_query_stats b
CROSS apply sys.dm_exec_sql_text(b.sql_handle) d
WHERE a.plan_handle = b.plan_handle
ORDER BY total_elapsed_time / execution_count DESC;
--总结:通过执行下面缓存计划可以看出,第一种完全使用了缓存计划,查询达到了很好的效果;
--而第二种则将缓存计划浪费了,导致缓存很快被占满,这种做法是相当不可取的
--=====================================
--7、 Left Join 的替代法
--测试一 执行计划:表扫描 -> 哈希匹配
SELECT A.ID,A.C1 FROM CHECK3_T1 A --2W行
LEFT JOIN CHECK3_T2 B ON A.ID=B.ID WHERE B.C1='C1' --400行
--测试二 执行计划:表扫描 -> 哈希匹配
SELECT A.ID,A.C1 FROM CHECK3_T1 A
RIGHT JOIN CHECK3_T2 B ON A.ID=B.ID WHERE a.C1='C1'
--测试三 执行计划:表扫描 -> 哈希匹配
SELECT A.ID,A.C1 FROM CHECK3_T1 A
INNER JOIN CHECK3_T2 B ON A.ID=B.ID WHERE B.C1='C1'
--总结:三条语句,在执行计划上完全一样,都是走的INNER JOIN的计划,
--因为测试一和测试二中,WHERE语句都包含了LEFT 和RIGHT表的字段,SQLSERVER若发现只要有这个表的字段,则会自动按照INNER JOIN进行处理
--补充测试:(1s)执行计划:表扫描-> 并行度 -> 位图 -> 排序 -> 合并联接 -> 并行度
SELECT A.ID,A.C1 FROM CHECK3_T2 A --400行
INNER JOIN CHECK3_T1 B ON A.ID=B.ID WHERE A.C1='C1' --2W行
--总结:这里有一个比较有趣的地方,若主表和关联表数据差别很大时,走的执行计划走的另一条路
--=====================================
--8、 ON(a.id=b.id AND a.tag=3)
--测试一
SELECT A.ID,A.C1 FROM CHECK3_T1 A
INNER JOIN CHECK3_T2 B ON A.ID=B.ID AND A.C1='C1'
--测试二
SELECT A.ID,A.C1 FROM CHECK3_T1 A
INNER JOIN CHECK3_T2 B ON A.ID=B.ID WHERE A.C1='C1'
--总结:内连接:无论是左表和右表的筛选条件都可以放到WHERE子句中
--测试一
SELECT A.ID,A.C1,B.C1 FROM CHECK3_T1 A
LEFT JOIN CHECK3_T2 B ON A.ID=B.ID AND B.C1='C1'
--测试二
SELECT A.ID,A.C1,B.C1 FROM CHECK3_T1 A
LEFT JOIN CHECK3_T2 B ON A.ID=B.ID WHERE B.C1='C1'
--总结:左外连接:当右表中的过滤条件放入ON子句后和WHERE子句后的结果不一样
--=====================================
--9、 赋值给变量,加Top 1
--测试一:(3s) 执行计划:表扫描
DECLARE @ID INT
SELECT @ID=ID FROM CHECK1_T1 WHERE C1='C1'
SELECT @ID
--测试二:(0s)执行计划:表扫描-> 前几行
DECLARE @ID INT
SELECT TOP 1 @ID=ID FROM CHECK1_T1 WHERE C1='C1'
SELECT @ID
--总结:给变量赋值最好都加上TOP 1,一从查询效率上增强,二为了准确性,若表CHECK1_T1有多个值,则会取最后一条记录赋给@ID
--=====================================
--10、 考虑是否适合用CASE语句
DECLARE @S INT=1
SELECT * FROM CHECK5_T1
WHERE C1=(CASE @S WHEN 1 THEN C1 ELSE 'C2' END)
SELECT * FROM CHECK5_T1
WHERE @S=1 OR C1='C2'
/*--=====================================
、检查语句是否需要Distinct. 执行计划:表扫描-> 哈希匹配-> 并行度-> 排序
select distinct c1 from CHECK3_T1
、禁用Select *,指定具体列名
select c1 from CHECK4_T1
select * from CHECK4_T1
、Insert into Table(*),指定具体的列名
、Isnull,没有必要的时候不要对字段使用isnull,同样会产生无法有效利用索引的问题,
和避免在筛选列上使用函数同样的原理。
、嵌套子查询,加上查询条件,确保子查询的结果集最小
--=====================================*/

MySQL은 초보자가 데이터베이스 기술을 배우는 데 적합합니다. 1. MySQL 서버 및 클라이언트 도구를 설치하십시오. 2. SELECT와 같은 기본 SQL 쿼리를 이해하십시오. 3. 마스터 데이터 작업 : 데이터를 만들고, 삽입, 업데이트 및 삭제합니다. 4. 고급 기술 배우기 : 하위 쿼리 및 창 함수. 5. 디버깅 및 최적화 : 구문 확인, 인덱스 사용, 선택*을 피하고 제한을 사용하십시오.

MySQL은 테이블 구조 및 SQL 쿼리를 통해 구조화 된 데이터를 효율적으로 관리하고 외래 키를 통해 테이블 간 관계를 구현합니다. 1. 테이블을 만들 때 데이터 형식을 정의하고 입력하십시오. 2. 외래 키를 사용하여 테이블 간의 관계를 설정하십시오. 3. 인덱싱 및 쿼리 최적화를 통해 성능을 향상시킵니다. 4. 데이터 보안 및 성능 최적화를 보장하기 위해 데이터베이스를 정기적으로 백업 및 모니터링합니다.

MySQL은 웹 개발에 널리 사용되는 오픈 소스 관계형 데이터베이스 관리 시스템입니다. 주요 기능에는 다음이 포함됩니다. 1. 다른 시나리오에 적합한 InnoDB 및 MyISAM과 같은 여러 스토리지 엔진을 지원합니다. 2.로드 밸런싱 및 데이터 백업을 용이하게하기 위해 마스터 슬레이브 복제 기능을 제공합니다. 3. 쿼리 최적화 및 색인 사용을 통해 쿼리 효율성을 향상시킵니다.

SQL은 MySQL 데이터베이스와 상호 작용하여 데이터 첨가, 삭제, 수정, 검사 및 데이터베이스 설계를 실현하는 데 사용됩니다. 1) SQL은 Select, Insert, Update, Delete 문을 통해 데이터 작업을 수행합니다. 2) 데이터베이스 설계 및 관리에 대한 생성, 변경, 삭제 문을 사용하십시오. 3) 복잡한 쿼리 및 데이터 분석은 SQL을 통해 구현되어 비즈니스 의사 결정 효율성을 향상시킵니다.

MySQL의 기본 작업에는 데이터베이스, 테이블 작성 및 SQL을 사용하여 데이터에서 CRUD 작업을 수행하는 것이 포함됩니다. 1. 데이터베이스 생성 : createAbasemy_first_db; 2. 테이블 만들기 : CreateTableBooks (idintauto_incrementprimarykey, titlevarchar (100) notnull, authorvarchar (100) notnull, published_yearint); 3. 데이터 삽입 : InsertIntobooks (Title, Author, Published_year) VA

웹 응용 프로그램에서 MySQL의 주요 역할은 데이터를 저장하고 관리하는 것입니다. 1. MySQL은 사용자 정보, 제품 카탈로그, 트랜잭션 레코드 및 기타 데이터를 효율적으로 처리합니다. 2. SQL 쿼리를 통해 개발자는 데이터베이스에서 정보를 추출하여 동적 컨텐츠를 생성 할 수 있습니다. 3.mysql은 클라이언트-서버 모델을 기반으로 작동하여 허용 가능한 쿼리 속도를 보장합니다.

MySQL 데이터베이스를 구축하는 단계에는 다음이 포함됩니다. 1. 데이터베이스 및 테이블 작성, 2. 데이터 삽입 및 3. 쿼리를 수행하십시오. 먼저 CreateAbase 및 CreateTable 문을 사용하여 데이터베이스 및 테이블을 작성한 다음 InsertInto 문을 사용하여 데이터를 삽입 한 다음 최종적으로 SELECT 문을 사용하여 데이터를 쿼리하십시오.

MySQL은 사용하기 쉽고 강력하기 때문에 초보자에게 적합합니다. 1.MySQL은 관계형 데이터베이스이며 CRUD 작업에 SQL을 사용합니다. 2. 설치가 간단하고 루트 사용자 비밀번호를 구성해야합니다. 3. 삽입, 업데이트, 삭제 및 선택하여 데이터 작업을 수행하십시오. 4. Orderby, Where and Join은 복잡한 쿼리에 사용될 수 있습니다. 5. 디버깅은 구문을 확인하고 쿼리를 분석하기 위해 설명을 사용해야합니다. 6. 최적화 제안에는 인덱스 사용, 올바른 데이터 유형 선택 및 우수한 프로그래밍 습관이 포함됩니다.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

WebStorm Mac 버전
유용한 JavaScript 개발 도구

SublimeText3 Linux 새 버전
SublimeText3 Linux 최신 버전

Atom Editor Mac 버전 다운로드
가장 인기 있는 오픈 소스 편집기

SublimeText3 영어 버전
권장 사항: Win 버전, 코드 프롬프트 지원!

Eclipse용 SAP NetWeaver 서버 어댑터
Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.
