기억의 실마리
2023. 2. 2. 00:12

이 게시글에 모든 예시는 아래 링크를 통해서 사용해 볼 수 있다.스키마 이름이나 칼럼이름 역시 같아 결과값이 그대로 적용되어 보여진다.

https://www.w3schools.com/mysql/trymysql.asp?filename=trysql_select_all

 

MySQL Tryit Editor v1.0

WebSQL stores a Database locally, on the user's computer. Each user gets their own Database object. WebSQL is supported in Chrome, Safari, and Opera. If you use another browser you will still be able to use our Try SQL Editor, but a different version, usin

www.w3schools.com

 

비상관 서브쿼리

연계되지않고 독립적으로 반환되는 서브쿼리를 뜻한다.

 

예제

SELECT
  CategoryID, CategoryName, Description,
  (SELECT ProductName FROM Products WHERE ProductID = 1)
FROM Categories;
-- 위의 Category ID, NAME, DESCRIPTION 모두 Categories 테이블에
-- 속하는 데이터이지만 괄호안에 서브쿼리는 Products 테이블에서 가져온 데이터이다.


SELECT * FROM Products
WHERE Price < (
  SELECT AVG(Price) FROM Products
);
-- Price 가 Products테이블에서 평균 Price보다 낮은 경우만 보여준다.


SELECT
  CategoryID, CategoryName, Description
FROM Categories
WHERE
  CategoryID =
  (SELECT CategoryID FROM Products
  WHERE ProductName = 'Chais');
-- Products 테이블에서 CategoryID기준으로 ProductName = 'Chais'인 데이터를
-- 가져와서 Categories테이블에서 CategoryID와 같은 CategoryID, CategoryName, Description의 데이터를 보여준다.


SELECT
  CategoryID, CategoryName, Description
FROM Categories
WHERE
  CategoryID IN
  (SELECT CategoryID FROM Products
  WHERE Price > 50);
-- 위와 마찬가지로 역순으로 괄호부터 해석해나가면 좀 더 쉽게 접근이 가능하다.

 

ALL, ANY

  • ~ ALL(서브쿼리) = 서브쿼리의 모든 결과에 대해 ~하다
  • ~ ANY(서브쿼리) = 서브쿼리의 하나 이상의 결과에 대해 ~하다

 

연산자 + ANY(SUBQUERY)

> ANY >= ANY < ANY <= ANY = ANY != ANY
최소값보다 크면 최소값보다 크거나 같으면 최대값보다 작으면 최대값보다 작거나 같으면 IN과 같은 기능 NOT IN과 같은
기능

 

연산자 + ALL(SUBQUERY)

> ALL >= ALL < ALL <= ALL = ALL != ALL
최소값보다 크면 최소값보다 크거나 같으면 최대값보다 작으면 최대값보다 작거나 같으면 서브 쿼리의
결과가 1건이면
괜찮지만 여러 건이면 오류가 발생
서브 쿼리의
결과가 1건이면
괜찮지만 여러 건이면 오류가 발생

 

예제

SELECT * FROM Products
WHERE Price > ALL (
  SELECT Price FROM Products
  WHERE CategoryID = 2
);
-- 서브쿼리 = CategoryID가 2인 Price들을 모두 가져온다. 그리고
-- 그 모든 데이터 들중 어떤 것보다(ALL의 의미이며 현재는 수의 비교이기 때문에
-- 데이터들 중에서 가장 큰 값이 기준이 된다.) Price가 큰 것을 가져온다.


SELECT
  CategoryID, CategoryName, Description
FROM Categories
WHERE
  CategoryID = ANY
  (SELECT CategoryID FROM Products
  WHERE Price > 50);
-- 위와 같은 조건인 경우 in을 쓰는게 더 적합하다.
-- ANY 의 경우는 IN으로 사용하면 같은 결과를 반환한다.

 

 

상관 서브쿼리

반환되는 쿼리로부터 연계되는 서브쿼리를 뜻한다.

 

예제

SELECT
  ProductID, ProductName,
  (
    SELECT CategoryName FROM Categories C
    WHERE C.CategoryID = P.CategoryID
  ) AS CategoryName
FROM Products P;
-- Categories C 이것은 AS를 생략한 문구로 C로 별명을 지어준 것이다.
-- 마찬가지로 Products P를 통해서 P로 별명을 만들어준 후 P. 으로 CategoryID에
-- 접근하고 C. 을 통해 CategoryID에 접근한 것이다. AS를 생략할 경우에는 절대로
-- 문자열을 사용하면 안된다. 공백 또한 안된다. 공백을 넣고 싶다면 백틱으로 별명을
-- 지어주고 접근할때역시 백틱을 사용해줘야 한다.

 

EXISTS / NOT EXISTS 연산자

예제

SELECT
  CategoryID, CategoryName
  -- ,(SELECT MAX(P.Price) FROM Products P
  -- WHERE P.CategoryID = C.CategoryID
  -- ) AS MaxPrice
FROM Categories C
WHERE EXISTS (
  SELECT * FROM Products P
  WHERE P.CategoryID = C.CategoryID
  AND P.Price > 80
);
-- EXISTS의 서브쿼리에 맞게 존재하는 결과들만 반환한다.

 

JOIN(INNER JOIN) 내부조인

 

예제

SELECT * FROM Categories C
JOIN Products P 
  ON C.CategoryID = P.CategoryID; 
-- Categories 와 Products 두 테이블을 합쳐준다(JOIN) 어떤 기준으로?(ON)
-- 각 테이블의 CategoryID를 기준으로 합쳐준다.

SELECT C.CategoryID, C.CategoryName, P.ProductName
FROM Categories C
JOIN Products P 
  ON C.CategoryID = P.CategoryID; 
-- ambiguous 주의!(같은 이름의 column이 있는경우 뜨는 에러)
-- 조인으로 Products에서 Categories테이블의 CategoryID와 Products의
-- CategoryID가 같은경우만 반환하고 그 후 P. 으로 접근하여 ProductName를 반환한다.

 

여러 테이블 JOIN

SELECT 
  C.CategoryID, C.CategoryName, 
  P.ProductName, 
  O.OrderDate,
  D.Quantity
FROM Categories C
JOIN Products P 
  ON C.CategoryID = P.CategoryID
JOIN OrderDetails D
  ON P.ProductID = D.ProductID
JOIN Orders O
  ON O.OrderID = D.OrderID;
-- 다른 여러테이블의 데이터가 필요할 경우 여러테이블을 JOIN하여 사용할 수 있다.

 

JOIN 테이블 GROUP하기

SELECT 
  C.CategoryName,
  MIN(O.OrderDate) AS FirstOrder,
  MAX(O.OrderDate) AS LastOrder,
  SUM(D.Quantity) AS TotalQuantity
FROM Categories C
JOIN Products P 
  ON C.CategoryID = P.CategoryID
JOIN OrderDetails D
  ON P.ProductID = D.ProductID
JOIN Orders O
  ON O.OrderID = D.OrderID
GROUP BY C.CategoryID;
-- C.CategoryID의 기준으로 GROUPING 해줬다. C.CategoryID는 1~8까지
-- 중복되는 숫자가 없고 종류별로 있기 때문에 종류별로 퍼스트,라스트오더와 팔린갯수를
-- 모두 볼 수 있다.


SELECT 
  C.CategoryName, P.ProductName,
  MIN(O.OrderDate) AS FirstOrder,
  MAX(O.OrderDate) AS LastOrder,
  SUM(D.Quantity) AS TotalQuantity
FROM Categories C
JOIN Products P 
  ON C.CategoryID = P.CategoryID
JOIN OrderDetails D
  ON P.ProductID = D.ProductID
JOIN Orders O
  ON O.OrderID = D.OrderID
GROUP BY C.CategoryID, P.ProductID;
-- C.CategoryID를 첫 기준으로 오름차순으로 정렬 된 후 P.ProductID도
-- 오름차순으로 정렬한다.

 

SELF JOIN - 같은 테이블끼리 JOIN

SELECT
  E1.EmployeeID, CONCAT_WS(' ', E1.FirstName, E1.LastName) AS Employee,
  E2.EmployeeID, CONCAT_WS(' ', E2.FirstName, E2.LastName) AS NextEmployee
FROM Employees E1 JOIN Employees E2
ON E1.EmployeeID + 1 = E2.EmployeeID;

-- 1번의 전, 마지막 번호의 다음이 없다.(외부 조인으로 해결가능)
-- 위와같이 같은 테이블이지만 별명을 다르게 만들어줘서 셀프조인이 가능하다.

 

LEFT/RIGHT OUTER JOIN - 외부 조인

SELECT
  E1.EmployeeID, CONCAT_WS(' ', E1.FirstName, E1.LastName) AS Employee,
  E2.EmployeeID, CONCAT_WS(' ', E2.FirstName, E2.LastName) AS NextEmployee
FROM Employees E1
LEFT JOIN Employees E2
ON E1.EmployeeID + 1 = E2.EmployeeID
ORDER BY E1.EmployeeID;

-- LEFT를 RIGHT로 바꿔서도 실행해 볼 것
-- 반환된 양쪽 데이터에서 오른쪽이나 왼쪽데이터가 NULL이라도 반환한다.


SELECT
  C.CustomerName, S.SupplierName,
  C.City, C.Country
FROM Customers C
LEFT JOIN Suppliers S
ON C.City = S.City AND C.Country = S.Country;

-- LEFT는 SupplierName을 가지지 않은 CustomerName도 반환한다. RIGHT는 반대이다.


SELECT
  IFNULL(C.CustomerName, '-- NO CUSTOMER --'),
  IFNULL(S.SupplierName, '-- NO SUPPLIER --'),
  IFNULL(C.City, S.City),
  IFNULL(C.Country, S.Country)
FROM Customers C
LEFT JOIN Suppliers S
ON C.City = S.City AND C.Country = S.Country;

-- LEFT를 RIGHT로 바꿔서도 실행해 볼 것
-- IFNULL을 사용해서 NULL일 경우의 반환 될 값을 넣어 줄 수 있다.

 

CROSS JOIN - 교차조인

SELECT
  E1.LastName, E2.FirstName
FROM Employees E1
CROSS JOIN Employees E2
ORDER BY E1.EmployeeID;
-- 직원들의 각각 LastName에 이름을 추가한 데이터를 모두 조합한다. 직원은 모두 9명이 있는데
-- LastName + FirstName을 반복 조합하기 때문에 총 81개의 이름조합 데이터를 가져온다.

 

UNION - 집합으로 다루기

  • UNION = 중복을 제거한 집합
  • UNION ALL = 중복을 제거하지 않은 집합
SELECT CustomerName AS Name, City, Country, 'CUSTOMER'
FROM Customers
UNION
SELECT SupplierName AS Name, City, Country, 'SUPPLIER'
FROM Suppliers
ORDER BY Name;
-- 위에 쿼리와 아래 쿼리를 같은 별명으로 만들어 줘서 합친 후에 모두 불러오는 기능이다.

 

합집합

SELECT CategoryID AS ID FROM Categories
WHERE CategoryID > 4
UNION
SELECT EmployeeID AS ID FROM Employees
WHERE EmployeeID % 2 = 0;

-- UNION ALL로 바꿔볼 것

-- CategoryID는 1~8이고 EmployeeID는 1~9이다. UNION을 사용했기 때문에 5, 6, 7, 8
-- 그리고 EmployeeID의 2의 배수인 2, 4를 반환한다. 그리고 6, 8은 중복이기 때문에 생략됐다.

-- 만약 UNION ALL 을 사용하게 되면 합집합. 즉, 생략하지 않고 모두 반환하기 때문에
-- 값은 5, 6, 7, 8, 2, 4, 6, 8 을 반환하게 된다.

 

 

교집합

SELECT CategoryID AS ID
FROM Categories C, Employees E
WHERE 
  C.CategoryID > 4
  AND E.EmployeeID % 2 = 0
  AND C.CategoryID = E.EmployeeID;
-- FROM에서 Employees E를 선언해 준 것은 WHERE에서 써먹기 위함이고 조건으로
-- C.CategoryID = E.EmployeeID 를 추가해서 교집합을 가져온다.

 

차집합

SELECT CategoryID AS ID
FROM Categories
WHERE 
  CategoryID > 4
  AND CategoryID NOT IN (
    SELECT EmployeeID
    FROM Employees
    WHERE EmployeeID % 2 = 0
  );
-- 조건을 통해서 차집합을 만들어준다.

 

대칭 차집합

SELECT ID FROM (
  SELECT CategoryID AS ID FROM Categories
  WHERE CategoryID > 4
  UNION ALL
  SELECT EmployeeID AS ID FROM Employees
  WHERE EmployeeID % 2 = 0
) AS Temp 
GROUP BY ID HAVING COUNT(*) = 1;
-- 조건을 통해서 두 쿼리사이에 교집합이 아닌 모든 것들을 반환하게 만들었다.

 

마치며...

MySQL에 대한 기술포스팅은 여기까지 해야겠다... 무궁무진하게 많기 때문에 가장 자주 쓰일 것 같은 내용만 포스팅 했다. 실제로 MySQL내에서도 사용하면서 응용만 한다면 어떤 데이터든 가져 올 수 있겠다는 자신감 조금은 생긴 것 같다.

 

예시자료 출처 -  https://www.yalco.kr/lectures/sql/

'Backend > MySQL' 카테고리의 다른 글

[ MySQL ] 데이터베이스 SELECT  (0) 2023.01.30
[ MySQL ] 데이터베이스  (4) 2023.01.29
2023. 1. 29. 23:25

데이터베이스

데이터베이스란 여러 사람이나 프로그램이 데이터를 쉽게 공유하기 위해
체계적으로 관리되는 데이터의 집합을 칭한다.

 

DBMS - 데이터베이스 관리 시스템

  • SQL DB (관계형 데이터베이스)
    행과 열로 이루어저 있다.
    엑셀처럼(표형식) 스키마(Schema: 구조와 제약 조건에 관한 명세)
    형식에 맞춰서 데이터가 저장된다.
    그러므로 체계적이고 안정적으로 데이터를 관리할 수 있다.
    대표적으로 PostgreSQL, MySQL, OracleDB, SQLite, MariaDB 가 있다.

  • NoSQL DB (관계형이 아닌 모든 데이터베이스)

    Key-Value : 파이썬의 딕셔너리처럼 키와 벨류로 데이터를 저장하는
    가장 단순한 데이터베이스다. ex) { 'name' : 'zeriong' }
    단순한 만큼 속도가 빠르고 주로 캐싱, 세션관리, 리더보드, 실시간분석 등에
    주로 사용된다.
    대표적인 제품으로 redis , amazon DynamoDB 등이 있다.

    Document : key와 value가 있는건 마찬가지 이지만 json의 형식으로 데이터를
    저장할 수 있는 Document형식의 데이터베이스다.Document형식이기 때문에
    스키마가 변경되는 것에 유연하게 대처할 수 있다.
    대표적으로 mongoDB, Amazon DocumentDB, Firestore 등이 있다.

    Gracph : 노드의 속성별로 데이터를 저장하는 방식이다.
    페이스북같은 소셜 네트워크 혹은 추천엔진을 위한 데이터베이스다.
    대표적으로 neo4j, Amazon Neptune, Giraph 등이 있다.

 

 

MySQL 시작하기

https://dev.mysql.com/downloads/installer/

 

MySQL :: Download MySQL Installer

Select Operating System: Select Operating System… Microsoft Windows Select OS Version: All Windows (x86, 32-bit) Windows (x86, 32-bit), MSI Installer 8.0.32 2.4M (mysql-installer-web-community-8.0.32.0.msi) MD5: 0f882590f8338adc614e9dc5cb00ca0b | Signatu

dev.mysql.com

링크를 통해서 용량이 높은 (mysql-installer-community-8.0.32.0.msi) 를 다운로드한다.

 

기본설정

  1. MySQL Workbench를 실행하고 localhost에서 설정했던 비밀번호로 root 계정에 접속한다.
  2. Add SQL아이콘을 눌러서 SQL을 추가할 수 있는 SQL File을 띄워준다.

  3. 아래의 코드를 입력하여 스키마를 만들어준다.
    CREATE SCHEMA `mydatabase` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci ;
    위 코드에서 utf8mb4 는 한글을 포함한 전세계문자와 이모티콘사용이 가능해진다.
    utf8mb4 _ general _ ci 는 정확도가 가장 높지는 않지만 정렬속도가 빠른 코드이다.

원하는 테이블에서 쿼리를 실행하고자 할땐 반드시 스키마리스트에서

해당 테이블을 더블클릭한 후 +SQL을 눌러야 해당 테이블내에서 쿼리를

실행시켜 원하는 데이터를 가져올 수 있다.

 

MySQL서버 파워쉘로 열기

( CLI = Comend Line Interface )

C:\Program Files\MySQL\MySQL Server 8.0\bin 에서
./mysql -u root -p
입력한다. 내용은
-u 유저네임은 root이고 -p 패스워드를 사용한다는 뜻이다.
이 후 지정한 패스워드를 입력하고

show databases; 를 입력하면
데이터베이스 테이블이 쭉 나온다.

이 후 사용할 테이블을 use와 함께 입력하면 사용가능하다.
ex) use mydatabase

이 후에
Database changed
문구가 뜨면 쿼리를 사용해주면 결과가 선택한 테이블 내의

데이터가 적용되어 나온다.

 

 

CREATE TABLE -  테이블 만들기

CREATE TABLE people ( -- 테이블이름은 people
  person_id INT, -- 콜룸데이터명은 person_id이고 int속성이다.
  person_name VARCHAR(10), -- VARCHAR 가변길이 문자열 10byte까지
  age TINYINT, -- TINYINT 범위: 0~255 숫자
  birthday DATE -- 날짜
);

 

ALTER TABLE - 테이블 변경

-- 테이블명 변경
ALTER TABLE people RENAME TO  friends,

-- 컬럼 자료형 변경
CHANGE COLUMN person_id person_id TINYINT,

-- 컬럼명 변경
CHANGE COLUMN person_name person_nickname VARCHAR(10), 

-- 컬럼 삭제
DROP COLUMN birthday,

-- 컬럼 추가
ADD COLUMN is_married TINYINT AFTER age;

 

DROP TABLE - 테이블 삭제

DROP TABLE friends;
-- friends테이블을 삭제한다.

 

INSERT INTO - 데이터 삽입

INSERT INTO people
  (person_id, person_name, age, birthday)
  VALUES (1, '홍길동', 21, '2000-01-31');
-- 콜룸명과 순서에 맞게 VALUES에 넣어주면 데이터가 입력된다.

INSERT INTO people
  VALUES (2, '전우치', 18, '2003-05-12');
-- 모든 컬럼에 값 넣을 때는 컬럼명들 생략 가능

INSERT INTO people
  (person_id, person_name, birthday)
  VALUES (3, '임꺽정', '1995-11-04');
-- 일부 컬럼에만 값 넣기 가능 (NOT NULL은 생략 불가)

INSERT INTO people
  (person_id, person_name, age, birthday)
  VALUES (1, '임꺽정', '스물여섯', '1995-11-04');
-- 자료형에 맞지 않는 값은 오류 발생

INSERT INTO people
  (person_id, person_name, age, birthday)
  VALUES 
    (4, '존 스미스', 30, '1991-03-01'),
    (5, '루피 D. 몽키', 15, '2006-12-07'),
    (6, '황비홍', 24, '1997-10-30');
-- 여러 행을 한 번에 입력 가능

 

테이블 생성시 제약 넣기

CREATE TABLE people (
  person_id INT AUTO_INCREMENT PRIMARY KEY,
  person_name VARCHAR(10) NOT NULL,
  nickname VARCHAR(10) UNIQUE NOT NULL,
  age TINYINT UNSIGNED,
  is_married TINYINT DEFAULT 0
);

--AUTO_INCREMENT     = 새 행 생성시마다 자동으로 1씩 증가
--PRIMARY KEY	     = 중복 입력 불가, NULL(빈 값) 불가
--UNIQUE	      = 중복 입력 불가 (NULL값 가능)
--NOT NULL	      = NULL(빈 값) 입력 불가
--UNSIGNED	      = (숫자일시) 양수만 가능
--DEFAULT	      = 값 입력이 없을 시 기본값

= PRIMARY KEY (기본키) =
1. 테이블마다 하나만 가능
2. 기본적으로 인덱스 생성 (기본키 행 기준으로 빠른 검색 가능)
3. 보통 AUTO_INCREMENT와 함께 사용
(각 행을 고유하게 식별 가능 - 테이블마다 하나씩 둘 것)

 

 

숫자 자료형

< 숫자 자료형 표 >

자료형 바이트 SIGNED UNSIGNED
TINYINT 1 -128 ~ 127 0 ~ 255
SMALLINT 2 -32,768 ~ 32,767 0 ~ 65,535
MEDIUMINT 3 -8,388,608 ~ 8,388,607 0 ~ 16,777,215
INT 4 -2,147,483,648 ~ 2,147,483,647 0 ~ 4,294,967,295
BIGINT 8 -2^63 ~ 2^63 - 1 0 ~ 2^64 - 1

 

고정 소수점(Fixed Point) 수

  • 좁은 범위의 수 표현이 가능하며, 정확한 값을 칭한다.
  • DECIMAL( s, d ) 실수 부분 총 자릿수( s ) & 소수 부분 자릿수 ( d ) s 최대 65

    ex: DECIMAL(5, 2) 1번째인자가 5라서 숫자 5자릿수,
    2번째 인자가 2라서 소수점 2자릿수까지만 가능하다.
    12345, 1234.5, 123.45 이런식으로 가능하다 숫자가 5자리면 된다.

부동 소수점(Floating Point) 수

  • 넓은 범위의 수 표현 가능하며, 정확하지 않은 값이지만
    일반적으로 충분히 정확하다.

  • FLOAT
    -3.402...E+38 ~ -1.175...E-38 , 0 , 1.175...E-38 ~ 3.402...E+38

  • DOUBLE
    -1.797...E+308 ~ -2.225E-308 , 0 , 2.225...E-308 ~ 1.797...E+308

 

문자열

  • CHAR( s )
    설명 :  고정 사이즈 (남는 글자 공백으로 채움)
    차지하는 바이트 :  s (고정값)
    MAX 바이트 :  255

  • VARCHAR ( s )
    설명 :  가변 사이즈(남는 글자를 제외하고 맞춰진다.)
    차지하는 바이트 :  실제 글자 수(최대 s) + 1 (글자수 정보)
    MAX 바이트 :  65,535

 

VARCHAR와 CHAR 이해하기

검색시 CHAR가 더 빠르다
VARCHAR 컬럼 길이값이 4글자보다 적을 경우 CHAR로 자동 변환된다

VARCHAR는 남는 글자수를 없애는 처리를 추가로 하고 기본적으로

글자수 정보를 반드시 넣어야 함으로 1바이트가 반드시 추가된다. 

ex)
CHAR(4) :  { VALUE: 'ABCD' } = 4bytes
VARCHAR(4) :  { VALUE: 'ABCD' } = 5bytes

이렇게 상황에 따라 경제적으로 사용하는 것이 효율적이다.

 

< 텍스트 / 최대바이트 >

TINYTEXT 255
TEXT 65,535
MEDIUMTEXT 16,777,215
LONGTEXT 4,294,967,295

 

날짜와 시간

  • DATE
    YYYY-MM-DD

  • TIME
    HHH:MI:SS
    HHH: -838 ~ 838까지의 시간
  • DATETIME
    YYYY-MM-DD HH:MI:SS
    입력된 시간값을 그 자체로 저장 ex) String(1995-05-18)=> Date(1995-05-18)

  • TIMESTAMP
    YYYY-MM-DD HH:MI:SS
    MySQL이 설치된 컴퓨터의 시간대를 기준으로 저장

DATETIME와 TIMESTAMP의 사용은?

  • 시간 데이터를 가감없이 기록할 때 DATETIME
    역사나 계획일자처럼 정해져있는 시간을 기록할때 사용한다.
  • 시간 자동기록, 국제적인 서비스를 할 경우 TIMESTAMP 사용
    국제적인 sns는 시차가 나라별로 다르기 때문에 사용한다.

 

마치며...

다음 포스팅에도 간단한 예시들과 적당한 설명을 포스팅 할 예정이다. 생각보다 MySQL과 데이터베이스에 대해서는 쓸 내용이 많은 것 같다...

 

예시자료 출처 -  https://www.yalco.kr/lectures/sql/

 

얄코 홈

어려운 프로그래밍 개념들을 쉽게 설명해주는 유튜브 채널 '얄팍한 코딩사전'. 영상에서 다 알려주지 못한 정보들이나 자주 묻는 질문들의 답변들, 예제 코드들을 얄코에서 확인하세요!

www.yalco.kr

 

'Backend > MySQL' 카테고리의 다른 글

[ MySQL ] 상관,비상관 서브쿼리와 JOIN  (0) 2023.02.02
[ MySQL ] 데이터베이스 SELECT  (0) 2023.01.30