집계 함수는 계산 결과를 한 행으로 합치는 특성이 있는 반면에 윈도우 함수는 원래 행을 그대로 유지하는 특성이 있다
값과 순위를 매기는 함수가 대표적이다.
단계 2: (값) LAST VALUE(정해진 윈도우 프레임 내에서 마지막 행의 값을 반환)
SQL> SELECT Name, printf ( '%.f minutes', Milliseconds / 1000 / 60 ) AS Length, LAST_VALUE ( Name ) OVER ( ORDER BY Milliseconds RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS LongestTrack FROM tracks WHERE AlbumId = 4;
SQL> SELECT AlbumId, Name, printf ( '%.f minutes', Milliseconds / 1000 / 60 ) AS Length, LAST_VALUE ( Name ) OVER ( PARTITION BY AlbumId ORDER BY Milliseconds DESC RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS ShortestTrack FROM tracks;
단계 3: LEAD(직후에 물리적으로 떨어진 오프셋 값을 가져오기)
SQL> CREATE VIEW CustomerInvoices AS SELECT CustomerId, strftime('%Y',InvoiceDate) Year, SUM( total ) Total FROM invoices GROUP BY CustomerId, strftime('%Y',InvoiceDate);
SQL> SELECT * FROM CustomerInvoices ORDER BY CustomerId, Year, Total;
SQL> SELECT CustomerId, Year, Total, LEAD ( Total,1,0 ) OVER ( ORDER BY Year ) NextYearTotal FROM CustomerInvoices WHERE CustomerId = 1;
SQL> SELECT CustomerId, Year, Total, LEAD ( Total,1,0 ) OVER ( PARTITION BY CustomerId ORDER BY Year ) NextYearTotal FROM CustomerInvoices;
단계 4: (값) NTH_VALUE(정해진 윈도우 프레임 내에서 N번째 행의 값을 반환)
SQL> SELECT Name, Milliseconds Length, NTH_VALUE ( name,2 ) OVER ( ORDER BY Milliseconds DESC ) SecondLongestTrack FROM tracks;
SQL> SELECT AlbumId, Name, Milliseconds Length, NTH_VALUE ( Name,2 ) OVER ( PARTITION BY AlbumId ORDER BY Milliseconds DESC RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS SecondLongestTrack FROM tracks;
단계 5: (순위) NTILE(순서가 있는 결과 집합을 정해진 버킷에 나눠서 넣기)
SQL> SELECT Name, Milliseconds, NTILE ( 4 ) OVER ( ORDER BY Milliseconds ) LengthBucket FROM tracks WHERE AlbumId = 1;
SQL> SELECT AlbumId, Name, Milliseconds, NTILE ( 3 ) OVER ( PARTITION BY AlbumId ORDER BY Bytes ) SizeBucket FROM tracks;
단계 6: (순위) PERCENT_RANK(순위를 퍼센트로 보여줌)
SQL> SELECT Name, Milliseconds, PERCENT_RANK() OVER( ORDER BY Milliseconds ) LengthPercentRank FROM tracks WHERE AlbumId = 1;
SQL> SELECT Name, Milliseconds, printf('%.2f',PERCENT_RANK() OVER( ORDER BY Milliseconds )) LengthPercentRank FROM tracks WHERE AlbumId = 1;
SQL> SELECT AlbumId, Name, Bytes, printf('%.2f',PERCENT_RANK() OVER( PARTITION BY AlbumId ORDER BY Bytes )) SizePercentRank FROM tracks;
단계 7: (순위) RANK(순위를 보여줌, 중복 가능, 빈 순서 있음)
SQL> CREATE TABLE RankDemo ( Val TEXT );
SQL> INSERT INTO RankDemo(Val) VALUES('A'),('B'),('C'),('C'),('D'),('D'),('E');
SQL> SELECT * FROM RankDemo;
SQL> SELECT Val, RANK () OVER ( ORDER BY Val ) ValRank FROM RankDemo;
SQL> SELECT Name, Milliseconds, RANK () OVER ( ORDER BY Milliseconds DESC ) LengthRank FROM tracks;
SQL> SELECT Name, Milliseconds, AlbumId, RANK () OVER ( PARTITION BY AlbumId ORDER BY Milliseconds DESC ) LengthRank FROM tracks;
SQL> SELECT * FROM ( SELECT Name, Milliseconds, AlbumId, RANK () OVER ( PARTITION BY AlbumId ORDER BY Milliseconds DESC ) LengthRank FROM tracks ) WHERE LengthRank = 2;
SQL>
단계 8: (순위) ROW_NUMBER(연속적인 정수를 부여, 중복 불가능)
SQL> SELECT ROW_NUMBER () OVER ( ORDER BY Country ) RowNum, FirstName, LastName, country FROM customers;
SQL> SELECT ROW_NUMBER () OVER ( PARTITION BY Country ORDER BY FirstName ) RowNum, FirstName, LastName, country FROM customers;
SQL> SELECT * FROM ( SELECT ROW_NUMBER () OVER ( ORDER BY FirstName ) RowNum, FirstName, LastName, Country FROM customers ) WHERE RowNum > 20 AND RowNum <= 30
SQL> CREATE VIEW Sales AS SELECT CustomerId, FirstName, LastName, Country, SUM( total ) Amount FROM invoices INNER JOIN customers USING (CustomerId) GROUP BY CustomerId;
SQL> SELECT Country, FirstName, LastName, Amount FROM ( SELECT Country, FirstName, LastName, Amount, ROW_NUMBER() OVER ( PARTITION BY country ORDER BY Amount DESC ) RowNum FROM Sales ) WHERE RowNum = 1;
CREATE TRIGGER [IF NOT EXISTS] schema_ame.trigger_name
INSTEAD OF [DELETE | INSERT | UPDATE OF column_name]
ON table_name
BEGIN
-- insert code here
END;
단계 2: INSTEAD OF 트리거 예제
SQL> CREATE VIEW AlbumArtists( AlbumTitle, ArtistName ) AS SELECT Title, Name FROM Albums INNER JOIN Artists USING (ArtistId);
SQL> SELECT * FROM AlbumArtists;
SQL> INSERT INTO AlbumArtists(AlbumTitle,ArtistName) VALUES('Who Do You Trust?','Papa Roach');
SQL> CREATE TRIGGER insert_artist_album_trg INSTEAD OF INSERT ON AlbumArtists BEGIN INSERT INTO Artists(Name) VALUES(NEW.ArtistName); INSERT INTO Albums(Title, ArtistId) VALUES(NEW.AlbumTitle, last_insert_rowid()); END;
SQL> INSERT INTO AlbumArtists(AlbumTitle,ArtistName) VALUES('Who Do You Trust?','Papa Roach');
SQL> SELECT * FROM Artists ORDER BY ArtistId DESC;
원본 학습자료는 SQLite Trigger를 참고하고, 방송에 사용한 실제 실습 자료는 다음을 참고한다:
단계 1: 트리거 기본 형식
CREATE TRIGGER [IF NOT EXISTS] trigger_name
[BEFORE|AFTER|INSTEAD OF] [INSERT|UPDATE|DELETE]
ON table_name
[WHEN condition]
BEGIN
statements;
END;
단계 2: BEFORE INSERT 트리거 예제
SQL> CREATE TABLE leads ( id integer PRIMARY KEY, first_name text NOT NULL, last_name text NOT NULL, phone text NOT NULL, email text NOT NULL, source text NOT NULL );
SQL> CREATE TRIGGER validate_email_before_insert_leads BEFORE INSERT ON leads BEGIN SELECT CASE WHEN NEW.email NOT LIKE '%_@__%.__%' THEN RAISE (ABORT,'Invalid email address') END; END;
SQL> INSERT INTO leads (first_name,last_name,email,phone) VALUES('John','Doe','jjj','4089009334');
CREATE [TEMP] VIEW [IF NOT EXISTS] view_name[(column-name-list)]
AS
select-statement;
단계 2: 복잡한 질의를 단순화하기 위한 뷰 생성
SQL> SELECT trackid, tracks.name, albums.Title AS album, media_types.Name AS media, genres.Name AS genres FROM tracks INNER JOIN albums ON Albums.AlbumId = tracks.AlbumId INNER JOIN media_types ON media_types.MediaTypeId = tracks.MediaTypeId INNER JOIN genres ON genres.GenreId = tracks.GenreId;
SQL> CREATE VIEW v_tracks AS SELECT trackid, tracks.name, albums.Title AS album, media_types.Name AS media, genres.Name AS genres FROM tracks INNER JOIN albums ON Albums.AlbumId = tracks.AlbumId INNER JOIN media_types ON media_types.MediaTypeId = tracks.MediaTypeId INNER JOIN genres ON genres.GenreId = tracks.GenreId;
SQL> SELECT * FROM v_tracks;
단계 3: 전용 컬럼 이름을 제공하기 위한 뷰 생성
SQL> CREATE VIEW v_albums ( AlbumTitle, Minutes ) AS SELECT albums.title, SUM(milliseconds) / 60000 FROM tracks INNER JOIN albums USING ( AlbumId ) GROUP BY albums.title;
CREATE TABLE table_name(
...,
column_name data_type CHECK(expression),
...
);
CREATE TABLE table_name(
...,
CHECK(expression)
);
BEGIN;
-- create a new table
CREATE TABLE new_table (
[...],
CHECK ([...])
);
-- copy data from old table to the new one
INSERT INTO new_table SELECT * FROM old_table;
-- drop the old table
DROP TABLE old_table;
-- rename new table to the old one
ALTER TABLE new_table RENAME TO old_table;
-- commit changes
COMMIT;
단계 2: 컬럼 CHECK 예제
SQL> CREATE TABLE contacts ( contact_id INTEGER PRIMARY KEY, first_name TEXT NOT NULL, last_name TEXT NOT NULL, email TEXT, phone TEXT OT NULL CHECK (length(phone) >= 10) );
SQL> INSERT INTO contacts(first_name, last_name, phone) VALUES('John','Doe','408123456');
SQL> INSERT INTO contacts(first_name, last_name, phone) VALUES('John','Doe','(408)-123-456');
단계 3: 테이블 CHECK 예제
SQL> CREATE TABLE products ( product_id INTEGER PRIMARY KEY, product_name TEXT NOT NULL, list_price DECIMAL (10, 2) NOT NULL, discount DECIMAL (10, 2) NOT NULL DEFAULT 0, CHECK (list_price >= discount AND discount >= 0 AND list_price >= 0) );
SQL> INSERT INTO products(product_name, list_price, discount) VALUES('New Product #1',2000,1000);
SQL> INSERT INTO products(product_name, list_price, discount) VALUES('New Product #2',900,1000);
SQL> INSERT INTO products(product_name, list_price, discount) VALUES('New XFactor',1000,-10);
단계 4: 기존 테이블에 CHECK 제약 걸기
SQL> CREATE TABLE contacts ( contact_id INTEGER PRIMARY KEY, first_name TEXT NOT NULL, last_name TEXT NOT NULL, email TEXT, phone TEXT NOT NULL );
SQL> INSERT INTO contacts(first_name, last_name, phone) VALUES('John','Doe','(408)-123-456');
SQL> BEGIN;
SQL> CREATE TABLE new_contacts ( contact_id INTEGER PRIMARY KEY, first_name TEXT NOT NULL, last_name TEXT NOT NULL, email TEXT, phone TEXT NOT NULL CHECK (length(phone) >= 10) );
SQL> INSERT INTO new_contacts SELECT * FROM contacts;
FOREIGN KEY (foreign_key_columns)
REFERENCES parent_table(parent_key_columns)
ON UPDATE action
ON DELETE action;
참고: 3.6.19 버전 이후에 외래 키를 지원
주의: SQLite을 컴파일할 때 SQLITE_OMIT_FOREIGN_KEY나 SQLITE_OMIT_TRIGGER를 정의하면 외래 키 제약을 사용할 수 없다
외래 키를 보고, 끄고 켜는 방법:
SQL> PRAGMA foreign_keys;
SQL> PRAGMA foreign_keys = OFF;
SQL> PRAGMA foreign_keys = ON;
단계 2: 외래 키 예제
SQL> PRAGMA foreign_keys = ON;
SQL> CREATE TABLE suppliers ( supplier_id integer PRIMARY KEY, supplier_name text NOT NULL, group_id integer NOT NULL );
SQL> CREATE TABLE supplier_groups ( group_id integer PRIMARY KEY, group_name text NOT NULL );
SQL> DROP TABLE suppliers;
SQL> CREATE TABLE suppliers ( supplier_id INTEGER PRIMARY KEY, supplier_name TEXT NOT NULL, group_id INTEGER NOT NULL, FOREIGN KEY (group_id) REFERENCES supplier_groups (group_id) );
SQL> INSERT INTO supplier_groups (group_name) VALUES ('Domestic'), ('Global'), ('One-Time');
SQL> INSERT INTO suppliers (supplier_name, group_id) VALUES ('HP', 2);
SQL> INSERT INTO suppliers (supplier_name, group_id) VALUES('ABC Inc.', 4);
단계 3: 외래 키 제약 행위(SET NULL)
SQL> PRAGMA foreign_keys = ON;
SQL> DROP TABLE suppliers;
SQL> CREATE TABLE suppliers ( supplier_id INTEGER PRIMARY KEY, supplier_name TEXT NOT NULL, group_id INTEGER, FOREIGN KEY (group_id) REFERENCES supplier_groups (group_id) ON UPDATE SET NULL ON DELETE SET NULL );
SQL> INSERT INTO suppliers (supplier_name, group_id) VALUES('XYZ Corp', 3);
SQL> INSERT INTO suppliers (supplier_name, group_id) VALUES('ABC Corp', 3);
SQL> DELETE FROM supplier_groups WHERE group_id = 3;
SQL> SELECT * FROM suppliers;
단계 4: 외래 키 제약 행위(RESTRICT)
SQL> PRAGMA foreign_keys = ON;
SQL> DROP TABLE suppliers;
SQL> CREATE TABLE suppliers ( supplier_id INTEGER PRIMARY KEY, supplier_name TEXT NOT NULL, group_id INTEGER, FOREIGN KEY (group_id) REFERENCES supplier_groups (group_id) ON UPDATE RESTRICT ON DELETE RESTRICT );
SQL> INSERT INTO suppliers (supplier_name, group_id) VALUES('XYZ Corp', 1);
SQL> DELETE FROM supplier_groups WHERE group_id = 1;
SQL> DELETE FROM suppliers WHERE group_id =1;
SQL> DELETE FROM supplier_groups WHERE group_id = 1;
단계 5: 외래 키 제약 행위(CASCADE)
SQL> PRAGMA foreign_keys = ON;
SQL> DELETE FROM supplier_groups;
SQL> DROP TABLE suppliers;
SQL> CREATE TABLE suppliers ( supplier_id INTEGER PRIMARY KEY, supplier_name TEXT NOT NULL, group_id INTEGER, FOREIGN KEY (group_id) REFERENCES supplier_groups (group_id) ON UPDATE CASCADE ON DELETE CASCADE );
SQL> INSERT INTO suppliers (supplier_name, group_id) VALUES('XYZ Corp', 1);
SQL> INSERT INTO suppliers (supplier_name, group_id) VALUES('ABC Corp', 2);
SQL> UPDATE supplier_groups SET group_id = 100 WHERE group_name = 'Domestic';
SQL> SELECT * FROM suppliers;
SQL> DELETE FROM supplier_groups WHERE group_id = 2;
CREATE TABLE table_name(
column_1 INTEGER NOT NULL PRIMARY KEY,
...
);
CREATE TABLE table_name(
column_1 INTEGER NOT NULL,
column_2 INTEGER NOT NULL,
...
PRIMARY KEY(column_1,column_2,...)
);
PRAGMA foreign_keys=off;
BEGIN TRANSACTION;
ALTER TABLE table RENAME TO old_table;
-- define the primary key constraint here
CREATE TABLE table ( ... );
INSERT INTO table SELECT * FROM old_table;
COMMIT;
PRAGMA foreign_keys=on;
단계 2: 기본 키 예제
SQL> CREATE TABLE countries ( country_id INTEGER PRIMARY KEY, name TEXT NOT NULL );
SQL> CREATE TABLE languages ( language_id INTEGER, name TEXT NOT NULL, PRIMARY KEY (language_id) );
SQL> CREATE TABLE country_languages ( country_id INTEGER NOT NULL, language_id INTEGER NOT NULL, PRIMARY KEY (country_id, language_id), FOREIGN KEY (country_id) REFERENCES countries (country_id) ON DELETE CASCADE ON UPDATE NO ACTION, FOREIGN KEY (language_id) REFERENCES languages (language_id) ON DELETE CASCADE ON UPDATE NO ACTION );
SQL> INSERT INTO addresses ( house_no, street, city, postal_code, country ) VALUES ( '3960', 'North 1st Street', 'San Jose ', '95134', 'USA ' );
SQL> INSERT INTO people ( first_name, last_name, address_id ) VALUES ('John', 'Doe', 1);
SQL> DROP TABLE addresses;
단계 3: 기본 키 추가하기
SQL> CREATE TABLE cities ( id INTEGER NOT NULL, name text NOT NULL );
SQL> INSERT INTO cities (id, name) VALUES(1, 'San Jose');
SQL> PRAGMA foreign_keys=off;
SQL> BEGIN TRANSACTION;
SQL> ALTER TABLE cities RENAME TO old_cities;
SQL> CREATE TABLE cities ( id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL );
ALTER TABLE table_name
RENAME COLUMN current_name TO new_name;
단계 2: 테이블 열 이름 변경 예제
SQL> CREATE TABLE Locations( LocationId INTEGER PRIMARY KEY, Address TEXT NOT NULL, City TEXT NOT NULL, State TEXT NOT NULL, Country TEXT NOT NULL );
SQL> INSERT INTO Locations(Address,City,State,Country) VALUES('3960 North 1st Street','San Jose','CA','USA');
SQL> ALTER TABLE Locations RENAME COLUMN Address TO Street;
SQL> SELET * FROM Locations;
단계 3: 테이블 열 이름 변경 예제(2)
참고: 3.25.0 이전에 사용하는 옛날 방식
SQL> DROP TABLE IF EXISTS Locations;
SQL> CREATE TABLE Locations( LocationId INTEGER PRIMARY KEY, Address TEXT NOT NULL, State TEXT NOT NULL, City TEXT NOT NULL, Country TEXT NOT NULL );
SQL> INSERT INTO Locations(Address,City,State,Country) VALUES('3960 North 1st Street','San Jose','CA','USA');
SQL> BEGIN TRANSACTION;
SQL> CREATE TABLE LocationsTemp( LocationId INTEGER PRIMARY KEY, Street TEXT NOT NULL, City TEXT NOT NULL, State TEXT NOT NULL, Country TEXT NOT NULL );
SQL> INSERT INTO LocationsTemp(Street,City,State,Country)
SQL> SELECT Address,City,State,Country FROM Locations;
SQL> DROP TABLE Locations;
SQL> ALTER TABLE LocationsTemp RENAME TO Locations;
ALTER TABLE existing_table
RENAME TO new_table;
) [WITHOUT ROWID];
ALTER TABLE table_name
ADD COLUMN column_definition;
ALTER TABLE table_name
RENAME COLUMN current_name TO new_name;
-- disable foreign key constraint check
PRAGMA foreign_keys=off;
-- start a transaction
BEGIN TRANSACTION;
-- Here you can drop column
CREATE TABLE IF NOT EXISTS new_table(
column_definition,
...
);
-- copy data from the table to the new_table
INSERT INTO new_table(column_list)
SELECT column_list
FROM table;
-- drop the table
DROP TABLE table;
-- rename the new_table to the table
ALTER TABLE new_table RENAME TO table;
-- commit the transaction
COMMIT;
-- enable foreign key constraint check
PRAGMA foreign_keys=on;
단계 2: 테이블 이름 변경 예제
SQL> CREATE TABLE devices ( name TEXT NOT NULL, model TEXT NOT NULL, Serial INTEGER NOT NULL UNIQUE );
SQL> INSERT INTO devices (name, model, serial) VALUES('HP ZBook 17 G3 Mobile Workstation','ZBook','SN-2015');
SQL> ALTER TABLE devices RENAME TO equipment;
SQL> SELECT name, model, serial FROM equipment;
단계 3: 테이블 열 추가 예제
SQL> ALTER TABLE equipment ADD COLUMN location text;
단계 4: 테이블 열 삭제 예제
SQL> CREATE TABLE users( UserId INTEGER PRIMARY KEY, FirstName TEXT NOT NULL, LastName TEXT NOT NULL, Email TEXT NOT NULL, Phone TEXT NOT NULL );
SQL> CREATE TABLE contacts ( contact_id INTEGER PRIMARY KEY, first_name TEXT NOT NULL, last_name TEXT NOT NULL, email TEXT NOT NULL UNIQUE, phone TEXT NOT NULL UNIQUE );
SQL> CREATE TABLE groups ( group_id INTEGER PRIMARY KEY, name TEXT NOT NULL );
SQL> CREATE TABLE contact_groups( contact_id INTEGER, group_id INTEGER, PRIMARY KEY (contact_id, group_id), FOREIGN KEY (contact_id) REFERENCES contacts (contact_id) ON DELETE CASCADE ON UPDATE NO ACTION, FOREIGN KEY (group_id) REFERENCES groups (group_id) ON DELETE CASCADE ON UPDATE NO ACTION );