那些年遇到的SQL問題真的不少,各種類型的,有面試的時候遇到的SQL,有開發的時候遇到的,有性能優化的時候遇到的,還有在網上看到的各種資料的,林林總總,問題不少,這裏做個彙總,從以下幾個方面說下:1,各種SQL語句;2索引和SQL性能Explain;3 JOIN相關,爲了忘卻的的紀念:
1,各種常見SQL語句
- 各種Join:內聯接,外聯接(LEFT OUTER JOIN,RIGHT OUTER JOIN,FULL OUTER JOIN),交叉聯接(笛卡爾積)
SELECT a.*,b.* FROM bbb b,aaa a;SELECT a.*,b.* FROM aaa a CROSS JOIN bbb b;
SELECT * FROM aaa a NATURAL INNER JOIN bbb b;
A,B兩個表列名完全不同,上面的結果是相同的。注意第三個NATURAL INNER JOIN如果列名有相同,結果大不相同。
有這麼一個題目:一個叫 team 的表,裏面只有一個字段name, 一共有4 條紀錄,分別是a,b,c,d, 對應四個球隊,現在四個球隊進行比賽,用一條sql 語句顯示所有可能的比賽組合。
答:select a.name, b.name from team a, team b where a.name < b.name
- 行列轉換
SELECT a.name,SUM(CASE WHEN a.subject='語文' THEN a.score ELSE 0 END) AS 語文,SUM(CASE WHEN a.subject='數學' THEN a.score ELSE 0 END) AS 數學,SUM(CASE WHEN a.subject='英語' THEN a.score ELSE 0 END) AS 英語from a_student a GROUP BY a.name;
SELECT NAME, (select score from a_student m where m.subject='語文' and m.name=a.name) as 語文,(select score from a_student m where m.subject='數學' and m.name=a.name) as 數學,(select score from a_student m where m.subject='英語' and m.name=a.name) as 英語from a_student a group by NAME;
這個例子不多說了,關鍵是要明白group 之後是個什麼樣的結果,再來做些處理,順便看下case語句的寫法。可能還有其他的寫法,另外有pivot table的用法。
- 刪除重複數據
很明顯,第5條記錄和第一條重複了,刪除表中多餘的重複記錄(多個字段,例如name和subject),只留有rowid最小的記錄:
delete from a_student a where (a.name,a.subject) in (select NAME,subject from a_student group by NAME,subject having count(*) > 1) and rowid not in (select min(rowid) from a_student group by NAME,subject having count(*)>1)
- 樹形結構數據查詢
樹形結構的一般是用with 做個臨時表,然後父數據和子數據做個內連接,把臨時表查詢出來;
當然寫存儲過程也可以實現,或者用oracle 的start with condition connect by prior的語句也可以;
2,索引和SQL性能Explain
造了兩張表,表結構完全相同TEST_A100和TEST_A1000,只是數據量不同,前者塞入100萬數據,後者1000萬,後面的區域表大約1萬條記錄
create table TEST_A1000 (
ID number not null,
NAME varchar2(32),
CITY number,
PROVINCE number,
DOB date
);
建立區域表和序列:
create table TEST_AREA (
ID number not null,
NAME varchar2(32),
PROV_ID number not null,
detail varchar2(2000)
);
create sequence SEQ_TESTAREA
minvalue 1
maxvalue 999999999
start with 1
increment by 1
cache 1000;
create or replace procedure PROC_ISTAS
begin
for i in 1..10000000 LOOP
insert into TEST_A1000 SELECT SEQ_TESTA.Nextval,DBMS_RANDOM.STRING('A', 32),TRUNC(DBMS_RANDOM.VALUE(100, 1100)),TRUNC(DBMS_RANDOM.VALUE(0, 100)),to_date(2451984+TRUNC(DBMS_RANDOM.VALUE(0,365)),'J') from dual ;
IF MOD(i,10000)=0 THEN
--dbms_output.put_line('執行開始,時間: ' || sysdate);
commit;
END IF;
end loop;
end;
begin
PROC_IST;
end;
也可以這樣塞入數據(使用隨借函數和connect by):
insert into TEST_A100 SELECT SEQ_TESTA.Nextval,DBMS_RANDOM.STRING('A', 32),TRUNC(DBMS_RANDOM.VALUE(100, 1100)),TRUNC(DBMS_RANDOM.VALUE(0, 100)),to_date(2451984+TRUNC(DBMS_RANDOM.VALUE(0,365)),'J') from dual connect by level <= 1000000;
insert into TEST_A1000 SELECT SEQ_TESTA.Nextval,DBMS_RANDOM.STRING('A', 32),TRUNC(DBMS_RANDOM.VALUE(100, 1100)),TRUNC(DBMS_RANDOM.VALUE(0, 100)),to_date(2451984+TRUNC(DBMS_RANDOM.VALUE(0,365)),'J') from dual connect by level <= 10000000;
insert into TEST_AREA SELECT SEQ_TESTAREA.Nextval,DBMS_RANDOM.STRING('A', 32),TRUNC(DBMS_RANDOM.VALUE(0, 100)),DBMS_RANDOM.STRING('A', 1000) from dual connect by level <= 1100;
在表上面添加主鍵和索引:
alter table TEST_A1000
add constraint PK_TEST primary key (ID)
using index ;
-- Create/Recreate indexes
create index IDX_NAME on TEST_A1000 (NAME);
分別分析下面的SQL語句:
SELECT COUNT(*) FROM TEST_A1000 a WHERE a.name IS NOT NULL;
--IS NOT NULL 走索引,IS NULL不走索引;
SELECT * FROM TEST_A1000 a WHERE a.name!='EnWYrMbvoWfBJSzoOevCfLQVAgIaNeSj'; ----不等於不走索引
select * from TEST_A1000 where ID=10 or ID=20; --不走索引
select * from TEST_A1000 where ID IN (10,20,30); --走索引,如果ID不加主鍵也不走
select * from TEST_A1000 where NAME IN ('EnWYrMbvoWfBJSzoOevCfLQVAgIaNeSj'); --走索引
select * from TEST_A1000 where ID BETWEEN 10 AND 30; --走索引
select * from TEST_A1000 where ID/30=100; --走索引
select * from TEST_A1000 where substr(NAME,0,3)='EnW'; --不走索引
其他的圖片沒有一一貼出來,總之,不同的數據庫,不同的數據庫版本,不同的測試場景,可能結果都有所不同,不能道聽途說,不能人云亦云,絕知此事要躬行!
3,JOIN相關