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

객체(Object)

  • 정의
    객체는 관련된 데이터와 함수의 집합이다. 여러 데이터와 함수로 이루어져 있으며
    객체 내부에 존재할때는 주로 "프로퍼티" 또는 "메소드"라 부른다.

  • 속성
    여러 속성을 하나의 변수에 저장할 수 있도록 해주는 데이터 타입으로
    Key / Value 를 Pair로 저장할 수 있는 구조이다.
    추가내용: Javascript는 객체기반의 스크립트 언어이다.

 

객체 만들기

객체를 만들때는 변수를 선언해주고 {} 를 할당해주면 된다.

let obj = {};

 

표기법

  1. 마침표 표기법 (dot notation)
    프로퍼티 접근 연산자   .   을 사용한다.

  2. 대괄호 표기법 (bracket notation)
    프로퍼티 접근연산자   [  ]   를 사용한다.
    프로퍼티를 반드시 문자열로 표기해야 동작한다.
let obj = {
  name: 'bob smith'
};

// dot notation
console.log(obj.name);  // bob smith

// bracket notation
console.log(obj['name']); // bob smith

// bracket notation은 프로퍼티를 반드시 문자열로 표기해주어야 한다.

 

 

  • 우선 여러 기능을 사용해보기 위해서 예시데이터를 넣어 줬다.
let obj = {
        name: [
            "bob", "smith",
            {bob: {smith: {bobSmith: "is name"}}},
        ],
        age: 32,
        gender: 'male',
        interests: ['music', 'skiing'],
        bio: function() {
            alert(this.name[0] + ' ' + this.name[1] + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
        },
        greeting: function() {
            alert('Hi! I\'m ' + this.name[0] + '.');
        }
    };

 

1. obj객체에 프로퍼티 추가

/* 객체변수.새로만들어줄key = value; */
obj.tall = 179;


/* 함수형 프로퍼티 추가도 쉽게 가능하다 */
const sayY = obj.sayYeah = () => {     alert( 'Yeah~~~!!!' ) }; //새로운 객체멤버 생성

obj.sayYeah(); // Yeah~~~!!!

 

2. 객체 ↔ 스트링 변환

    const changeString = JSON.stringify(obj.name); // 객체를 스트링으로 변환.
    console.log("객체 => 스트링",changeString);
    // JSON.객체 => 스트링 ["bob","smith",{"bob":{"smith":{"bobSmith":"is name"}}}]

    const stringToJsonObject = JSON.parse(`{ "name": "John Doe", "age":30 }`); //스트링을 객체로 변환.
    console.log("스트링 => 객체",stringToJsonObject);
    // {name: 'John Doe', age: 30}
    //    age: 30
    //    name: "John Doe"
    // ▶ [[Prototype]] : Object

 

3. 객체 프로퍼티 삭제하기

예제 1

    console.log("name 삭제전, name:",obj.name);
    // name 삭제전, name:  ["bob", "smith", {...}]
    
    delete obj.name[0]; //프로퍼티 삭제
    
    console.log("name 삭제후, name:",obj.name);
    // name 삭제후, name:  ["비어있음", "smith", {...}]

예제2

    console.log("name 삭제전, name:",obj.name);
    /*
     name 삭제전, name:
     0: "bob"
     1: "smith"
     2:
       bob:
        smith: 
    */
    
    delete obj.name[2].bob.smith.bobSmith; //해당프로퍼티 삭제
    
    
    console.log("name 삭제후, name:",obj.name);
    /*
     name 삭제후, name:
     0: "bob"
     1: "smith"
     2:
       bob:
        smith: 
    */

 

실습 해보면서 특이한 점을 발견했다. 분명 삭제를 하기전과 삭제 후의 콘솔로그가 달라야할텐데...?

이상하게도 둘은 같은 로그를 나타내고 있었다. 이를 통해서 name 프로퍼티에 겹객체를 넣어 둔 것이

이러한 작동을 일으켰다는 것을 유추할 수 있었다.

 

  • 이를 통해 유추할 수 있는 내용...
    가장 먼저 선언된 일반적인 키를 삭제하는 경우엔 콘솔로그와 동기적으로 작동하지만
    객체 내부의 객체키(겹객체)를 삭제하는경우. 내부의 키가 모두 삭제된 이후에 콘솔로그가
    작동하는 것을 알 수 있었다. (콘솔로그와 비동기적으로 작동)
    이 현상에 대한 원인은 추 후 학습하게 된다면 따로 내용을 추가해야겠다.

혹시 이 게시글을 보고 계신분들중 원인을 알고 계신분은 댓글로 남겨주시면 감사드리겠습니다!

 

 

4. 구조분해할당을 통한 프로퍼티 분류

    const { name, ...others } = obj;
    
    console.log("name array :",name); // name array : (3) ['bob', 'smith', {…}]
    console.log("name :",name[0] + " " + name[1]); // name : bob smith
    
    console.log("others :",others);
    // others : {age: 32, gender: 'male', interests: Array(2), bio: ƒ, greeting: ƒ, …}

구조분해할당을 사용하여 name을 받고 spread연산자를 사용하여 name을 제외한

obj내의 모든 프로퍼티의 집합을 others라는 새로운 변수로서 할당시켜준 것이다.

 

5. 객체의 모든 key와 value

    const objKeys = Object.keys(obj);
    const objValues = Object.values(obj);
    
    console.log( objKeys );
    // (7) ['name', 'age', 'gender', 'interests', 'bio', 'greeting', 'sayYeah']
    
    console.log( objValues );
    /*
    (7) [Array(3), 32, 'male', Array(2), ƒ, ƒ, ƒ]
    0: (3) ['bob', 'smith', {…}]
    1: 32
    2: "male"
    3: (2) ['music', 'skiing']
    4: ƒ ()
    5: ƒ ()
    6: () => { alert( 'Yeah~~~!!!' ) }
    length: 7
    */

 

 

6. 프로퍼티의 존재여부 확인하기

    // includes를 통한 존재여부 확인
    const isExist_name = Object.keys(obj).includes('name');
    const isExist_hobby = Object.keys(obj).includes('hobby');

    console.log("------------------------------------");
    console.log("includes를 통한 존재여부 확인!");
    console.log("name의 존재여부 :",isExist_name,"\nhobby의 존재여부: ",isExist_hobby);
    console.log("------------------------------------");
    /*
    ------------------------------------
    includes를 통한 존재여부 확인!
    name의 존재여부 : true 
    hobby의 존재여부:  false
    ------------------------------------
    */
    
    
    // key in obj를 통한 존재여부 확인
    obj.hobby = undefined; //undefined값을 가진 프로퍼티 추가
    const isExist_name_in = 'name' in obj;
    const isExist_hobby_in = 'hobby' in obj;
    console.log("key in obj를 통한 존재여부 확인!");
    console.log("name의 존재여부 :",isExist_name_in,"\nhobby의 존재여부: ",isExist_hobby_in);
    console.log("------------------------------------");
    /*
    ------------------------------------
    key in obj를 통한 존재여부 확인!
    name의 존재여부 : true 
    hobby의 존재여부:  true
    ------------------------------------
    */
    //undefined 값을 가진다고 프로퍼티가 존재하지 않는 것이 아니다. 그러므로 true를 반환한다
    
    
    // 가장 정확한 존재여부 확인 방법은 hasOwnProperty함수를 사용하는 것이다.
    // 해당 객체의 직접적인 속성만을 검사하기 때문이다.
    const hasOwnName = obj.hasOwnProperty('name');
    const hasOwnHobby = obj.hasOwnProperty('hobby');
    console.log("hasOwnProperty를 통한 존재여부 확인!");
    console.log("name의 존재여부 :",hasOwnName,"\nhobby의 존재여부: ",hasOwnHobby);
    console.log("------------------------------------");
    /*
    ------------------------------------
    hasOwnProperty를 통한 존재여부 확인!
    name의 존재여부 : true 
    hobby의 존재여부:  true
    ------------------------------------
    */

위 내용에서 key in obj는 과거Object의 prototype 마저도 체크하기 때문에

프로퍼티의 존재여부를 크게 착각할 수도 있었다. 하지만 학습을 하는 과정에서는

적용 되지 않았고, Javascript Document에서 찾아보니 현재는 프로토타입체크를

지원하지 않게 되어 더 이상 상관없게 되었다.

 

마치며...

객체에 대해서 더 자세하게 학습을 진행하면서 웹개발이 주목적인 자바스크립트에선 학습우선순위 0순위이지 않을까? 라는 생각이 들었다. 그리고 데이터를 다루는 데에 있어 아주 유용한 기능들이 매우 편리하게 사용할 수 있도록 구현되어 있어 더욱 마음에 들었다. 다음포스팅은 객체와 마찬가지로 데이터다루기에 적합한 배열에 대해서 포스팅을 해봐야겠다.

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. 30. 21:34

MySQL의 SELECT기능 다뤄보기

이 게시글은 전반적으로  SELECT기능을 기술한 것이 대부분이다.

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

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

 

데이터 Customers에서 모든내용 보기

SELECT * FROM Customers;
-- 이와 같이 주석을 달 수 있다.

 

원하는 column(열)만 골라서 보기

SELECT CustomerName FROM Customers;
--Customers스키마에서 CustomerName의 열만 골라서 보여준다.

SELECT CustomerName, ContactName, Country
FROM Customers;
--이렇게 다중으로 볼 수도 있다.

 

테이블의 column이 아닌 값 보기

SELECT
  CustomerName, 1, 'Hello', NULL
FROM Customers;
-- 1과 Hello와 NULL은 테이블의 column이 아니다.
-- 하지만 위와같이 입력하면 해당 내용과 같은 것들만 보여진다.

 

원하는 row(행)만 선택해서 보기

SELECT * FROM Orders
WHERE EmployeeID = 3;
-- 데이터 Orders에서 모든것 중에서 WHERE(조건문)을
-- 사용해서 EmployeeID가 3인 행만 보여지게 된다.

SELECT * FROM OrderDetails 
WHERE Quantity < 5;
-- 이런식으로 조건문을 꼭 등호가 아니여도 된다.
-- if문처럼 TRUE or FALSE로 반환되도록 잘 붙여주면 된다.

 

원하는 데이터 순서대로 가져오기

ORDER BY 구문을 사용하여 특정 column을 기준으로 잡고
-- 오름, 내림차순으로 볼 수 있다. Default는 오름차순이다.
-- ASC = 오름차순  /  DESC = 내림차순

SELECT * FROM Customers
ORDER BY ContactName;
-- 아무것도 지정해주지 않았기 때문에 ContactName을 기준으로
-- 오름차순으로 정렬된다.

SELECT * FROM OrderDetails
ORDER BY ProductID ASC, Quantity DESC;
-- 이런 경우 먼저 나온 column이 먼저 기준이 되어 정렬되고
-- 먼저 정렬된 것을 기준으로 그 다음 column이 정렬된다.

 

원하는 만큼만 데이터 가져오기

-- LIMIT {가져올 갯수} 또는 LIMIT {건너뛸 갯수}, {가져올 갯수} 를 사용하여,
-- 원하는 위치에서 원하는 만큼만 데이터를 가져올 수 있다.

SELECT * FROM Customers
LIMIT 10;
-- (첫줄부터 10까지)

SELECT * FROM Customers
LIMIT 0, 10;
-- (0~10까지)

SELECT * FROM Customers
LIMIT 30, 10;
-- (30번째줄부터 시작해서 10줄까지)

 

원하는 별명(alias)으로 데이터 가져오기

SELECT
  CustomerId AS ID,
  CustomerName AS NAME,
  Address AS ADDR
FROM Customers;

SELECT
  CustomerId AS '아이디',
  CustomerName AS '고객명',
  Address AS '주소'
FROM Customers;

 

 

사칙연산

 

1. 기본 사칙연산

 

  • +, -, *, /
    각각 더하기, 빼기, 곱하기, 나누기
  • %, MOD
    나머지
SELECT 1 + 2;
SELECT 5 - 2.5 AS DIFFERENCE;
SELECT 3 * (2 + 4) / 2, 'Hello';
SELECT 10 % 3;

 

  • 문자열에 사칙연산을 가하면 0으로 인식한다.
SELECT 'ABC' + 3;
SELECT 'ABC' * 3;
SELECT '1' + '002' * 3;
-- 숫자로 구성된 문자열은 숫자로 자동인식

 

  • 또 다른 예제
SELECT
  OrderID + ProductID
FROM OrderDetails;
-- OrderID의 숫자와 ProductID의숫자가 더해진다.

SELECT
  ProductName,
  Price / 2 AS HalfPrice
FROM Products;
-- ProductName과 Price를 나누기 2한 결과값의
-- 데이터이름을 HalfPrice로 보여지게 했다.

 

 

2. 참/거짓 관련 연산자

 

  • MySQL에서는 TRUE는 1, FALSE는 0으로 저장됩니다.
SELECT TRUE, FALSE;
-- true = 1  , false = 0

 

  • 또 다른 예제
SELECT !TRUE, NOT 1, !FALSE, NOT FALSE;
-- !true = 0, not 1 = 0, !false = 1, not false = 1
-- 트루의 반대는 펄스라서 0, not은 아니다의 뜻

SELECT 0 = TRUE, 1 = TRUE, 0 = FALSE, 1 = FALSE;
-- 0, 1, 1, 0
-- 1=true이고 0=false이기 때문이다.

SELECT * FROM Customers WHERE City = 'Berlin';
-- 전체 데이터에서 where조건문에 의해서 시티가 베를린인 것을  select한다.

 

  • IS = 양쪽이 모두 TRUE 또는 FALSE
    IS NOT = 한쪽은 TRUE, 한쪽은 FALSE
SELECT TRUE IS TRUE;
-- true는 true가 맞다.  return 1;

SELECT TRUE IS NOT FALSE;
-- true는 false가 아니다. return 1;

SELECT (TRUE IS FALSE) IS NOT TRUE;
-- true는 false이다. <= 이것은 true가 아니다. return 1;

 

  • AND, && = 양쪽이 모두 TRUE일 때만 TRUE
    OR, || = 한쪽은 TRUE면 TRUE
    Javascript와 동일하게 작동한다.
SELECT TRUE AND FALSE, TRUE OR FALSE;
-- Javascript ver.
-- true && false = false, true || false = true

SELECT 2 + 3 = 6 OR 2 * 3 = 6;
-- 5와 6이지만 or을 사용했기때문에 true를 반환.

SELECT * FROM Orders
WHERE
  CustomerId = 15 AND EmployeeId = 4;
-- and를 썼기 때문에 두 조건 모두 충족하는 데이터만 보임.
-- CustomerId = 15 OR EmployeeId = 4; 이면 두 조건중 하나만
-- 충족해도 보여지게 된다.

SELECT * FROM Products 
WHERE
  ProductName = 'Tofu' OR CategoryId = 8;
-- 두 조건중 하나라도 충족한 경우 모두 보여진다.

SELECT * FROM OrderDetails
WHERE
  ProductId = 20
  AND (OrderId = 10514 OR Quantity = 50);
-- ProductId = 20이면서 OrderId = 10514이거나
-- Quantity = 50인 데이터를 모두 보여준다.

 

  • = 양쪽 값이 같음
    !=, <> 양쪽 값이 다름
    >, < (왼쪽, 오른쪽) 값이 더 큼
    >=, <= (왼쪽, 오른쪽) 값이 같거나 더 큼
SELECT 1 = 1, !(1 <> 1), NOT (1 < 2), 1 > 0 IS NOT FALSE;
-- 1, 1, 0, 1

SELECT 'A' = 'A', 'A' != 'B', 'A' < 'B', 'A' > 'B';
-- 1, 1, 1, 0
-- 알파뱃은 나중에 나오는 알파뱃일수록 크다고 판정된다.

-- MySQL의 기본 사칙연산자는 대소문자 구분을 하지 않는다.
SELECT 'A' = 'a';
-- true이다. 구분하지 않기때문에

SELECT
  ProductName, Price,
  Price > 20 AS EXPENSIVE 
FROM Products;
-- ProductName과 Price데이터를 보여주고
-- Price가 20보다 높으면 EXPENSIVE라는 별명에
-- 1이 추가된다 (20보다 높으면 참이 되기 때문)

SELECT
  ProductName, Price,
  NOT Price > 20 AS CHEAP 
FROM Products;
-- ProductName과 Price데이터를 보여주고
-- Price가 20보다 크지 않은 것(not)은 CHEAP이라는 별명에
-- 1이 추가 된다.

 

  • BETWEEN {MIN} AND {MAX} = 두 값 사이에 있다.
    NOT BETWEEN {MIN} AND {MAX} =  두 값 사이가 아닌 곳에 있다.
SELECT 5 BETWEEN 1 AND 10;
-- 5는 1과 10 사이에 있다. = true

SELECT 'banana' NOT BETWEEN 'Apple' AND 'camera';
-- a와 c사이에 b가 있는건 맞지만 not이 붙어서 false를 반환한다.

SELECT * FROM OrderDetails
WHERE ProductID BETWEEN 1 AND 4;
-- OrderDetails에서 ProductID가 1~4인 데이터만 보여진다.

SELECT * FROM Customers
WHERE CustomerName BETWEEN 'b' AND 'c';
-- b와 c사이는 b로 시작하는 모든 문장을 말한다.
-- c를 넘기진 말아야 하니 c로시작하는 문장은 제외된다.

 

  • IN (...) 괄호 안의 값들 가운데 있다.
    NOT IN (...) 괄호 안의 값들 가운데 없다.
SELECT 1 + 2 IN (2, 3, 4) 
-- 1+2 = 3 괄호안에 3 있으니까 true를 반환한다.

SELECT 'Hello' IN (1, TRUE, 'hello') 
-- hello가 있으니 true를 반환. 대소문자를 구분안한다는 것을 기억하자.

SELECT * FROM Customers
WHERE City IN ('Torino', 'Paris', 'Portland', 'Madrid') 
-- Customers데이터에서 City내부에 
-- 'Torino', 'Paris', 'Portland', 'Madrid'가 있는 데이터만 보여진다.

 

  • LIKE '... % ...' 0~N개 문자를 가진 패턴
    LIKE '... _ ...' _ 갯수만큼의 문자를 가진 패턴
SELECT
  'HELLO' LIKE 'hel%',   = 1
-- hel이후 어떤 문자가 와도 true

  'HELLO' LIKE 'H%',   = 1
-- h로 시작하고 그 이후 어떤 문자가 있던 true

  'HELLO' LIKE 'H%O',   = 1
-- H로 시작해서 O로 끝나기만 하면 true

  'HELLO' LIKE '%O',   = 1
-- 앞에 몇글자가 있든 무슨 글자가 있든 O로 끝나면 true

  'HELLO' LIKE '%HELLO%',  = 1
-- HELLO라는 글자 앞과 뒤에 어떤 문자가 얼마나 있든
-- HELLO만 있다면 true, 0~N개라서 아무것도 없이 HELLO여도 true

  'HELLO' LIKE '%H',   = 0 
-- H로 끝나야 true인데 HELLO이기때문에 O로 끝나서 false를 반환한다.

  'HELLO' LIKE 'L%'   = 0
-- L로 시작해야 하는데 HELLO라서 false를 반환한다.

 

연산자  총정리!

SELECT
  'HELLO' LIKE 'HEL__',    = 1
  'HELLO' LIKE 'h___O',    = 1
  'HELLO' LIKE 'HE_LO',    = 1
  'HELLO' LIKE '_____',    = 1
  'HELLO' LIKE '_HELLO',    = 0
  'HELLO' LIKE 'HEL_',    = 0
  'HELLO' LIKE 'H_O'    = 0
-- 무슨 글자든 _ 의 갯수에 맞춰서 트루와 펄스를 반환한다.


/*

[ +, -, *, / ] === 각각 더하기, 빼기, 곱하기, 나누기

[ %, MOD ] === 나머지

[ IS ] === 양쪽이 모두 TRUE 또는 FALSE

[ IS NOT] === 한쪽은 TRUE, 한쪽은 FALSE

[ AND, && ] === 양쪽이 모두 TRUE일 때만 TRUE

[ OR, || ]=== 한쪽은 TRUE면 TRUE

[ = ] === 양쪽 값이 같음

[ !=, <> ] === 양쪽 값이 다름

[ >, < ] ===(왼쪽, 오른쪽) 값이 더 큼

[ >=, <= ] === (왼쪽, 오른쪽) 값이 같거나 더 큼

[ BETWEEN {MIN} AND {MAX} ] === 두 값 사이에 있음

[ NOT BETWEEN {MIN} AND {MAX} ] === 두 값 사이가 아닌 곳에 있음

[ IN (...)] === 괄호 안의 값들 가운데 있음

[ NOT IN (...) ] === 괄호 안의 값들 가운데 없음

[ LIKE '... % ...' ] === 0~N개 문자를 가진 패턴

[ LIKE '... _ ...' ] === _ 갯수만큼의 문자를 가진 패턴

*/

 

 

 

 ※  예시가 너무 방대하여 이해가 난해한 경우만 간략한 예시를 작성했다.  ※

Math

  • ROUND = 반올림
    CEIL = 올림
    FLOOR = 내림

  • ABS = 절대값
  • GREATEST() = 가장 큰 값
  • LEAST() = 가장 작은 값

  • MAX()  = 가장 큰 값
    MIN()  = 가장 작은 값
    COUNT()  = 갯수 (NULL값 제외)
    SUM()  = 총합
    AVG()  = 평균 값

  • TRUNCATE(N, n) = N을 소숫점 n자리까지 선택

 

문자열 케이스

  • UCASE, UPPER = 모두 대문자로
    LCASE, LOWER = 모두 소문자로
  • CONCAT(...) = 괄호 안의 인자(내용)를 이어붙임
    SELECT CONCAT('HELLO', ' ', 'THIS IS ', 2021)
    = (HELLO THIS IS 2021) 숫자도 문자열로 자동변환되어 더해진다.

    CONCAT_WS(S, ...) = 첫번째 인자로 이어붙임
    SELECT CONCAT_WS('-', 2021, 8, 15, 'AM')
    = (2021-8-15-AM)

  • SUBSTR(), SUBSTRING() = 주어진 값에 따라 문자열 자름
    SELECT
    SUBSTR('ABCDEFG', 3),
    = 왼쪽에서 3번째 문자부터 읽어온다. (CDEFG)

    LEFT() = 왼쪽부터 N글자
    RIGHT() = 오른쪽부터 N글자

 

그룹함수

 

1. 예시

SELECT Country FROM Customers
GROUP BY Country;
-- 중복되는 것들을 모두 하나로 묶어서 하나씩만 보여진다.


SELECT 
  Country, City,
  CONCAT_WS(', ', City, Country) as 'city, country'
FROM Customers
GROUP BY Country, City;
-- GROUP BY를 컨트리와 시티에 해주어서 Country의 값이 같은
-- 값이라도 City의 값이 다르면 다 보여준다.

 

2. 그룹함수 활용하기

SELECT
  COUNT(*), OrderDate
FROM Orders
GROUP BY OrderDate;
-- 그룹으로 묶인 값들의 갯수도 같이 볼 수 있다.

SELECT
  ProductID,
  SUM(Quantity) AS QuantitySum
FROM OrderDetails
GROUP BY ProductID
ORDER BY QuantitySum DESC;

SELECT
  CategoryID,
  MAX(Price) AS MaxPrice, 
  MIN(Price) AS MinPrice,
  TRUNCATE((MAX(Price) + MIN(Price)) / 2, 2) AS MedianPrice,
  TRUNCATE(AVG(Price), 2) AS AveragePrice
FROM Products
GROUP BY CategoryID;

SELECT 
  CONCAT_WS(', ', City, Country) AS Location,
  COUNT(CustomerID)
FROM Customers
GROUP BY Country, City;

Tryit Editor을 통해서 사용해보면 결과값을 확인할 수 있다.

 

  • WITH ROLLUP = 전체의 집계값 (ORDER BY와는 사용불가능)
SELECT
  Country, COUNT(*)
FROM Suppliers
GROUP BY Country
WITH ROLLUP;
-- COUNT의 합계가 가장 밑에 추가된다.

 

  • HAVING = 그룹화된 데이터 걸러내기
SELECT
  Country, COUNT(*) AS Count
FROM Suppliers
GROUP BY Country
HAVING Count >= 3;
-- Count로 집계된 데이터중 3과 같거나 큰 값만 보여준다.

 

  • WHERE는 그룹하기 전 데이터, HAVING은 그룹 후 집계에 사용한다.
SELECT
  COUNT(*) AS Count, OrderDate
FROM Orders
WHERE OrderDate > DATE('1996-12-31')
GROUP BY OrderDate
HAVING Count > 2;
-- WHERE로 1996-12-31보다 높은 수의 날짜들로 거른 후
-- OrderDate를 그룹화 시켜준 후 그룹화가 되었으니 HAVING으로 2보다 높은 수만 보여준다.

SELECT
  CategoryID,
  MAX(Price) AS MaxPrice, 
  MIN(Price) AS MinPrice,
  TRUNCATE((MAX(Price) + MIN(Price)) / 2, 2) AS MedianPrice,
  TRUNCATE(AVG(Price), 2) AS AveragePrice
FROM Products
WHERE CategoryID > 2
GROUP BY CategoryID
HAVING
  AveragePrice BETWEEN 20 AND 30
  AND MedianPrice < 40;
-- 이와같은 예시로 WHERE로 먼저 데이터를 걸러준 후 GROUP BY를
-- 사용해서 묶어준 후 HAVING으로 걸러낼 수 있다.

 

  • DISTINCT = 중복된 값들을 제거한다.
    GROUP BY 와 달리 집계함수가 사용되지 않는다.
    GROUP BY 와 달리 정렬하지 않으므로 더 빠르다.
SELECT DISTINCT CategoryID
FROM Products;
-- 위의 GROUP BY를 사용한 쿼리와 결과 비교
-- GROUP BY는 오름차순(Default)으로 정렬되어 보여주지만 DISTINCT는
-- 정렬하지 않은채로 데이터를 보여준다.

SELECT COUNT DISTINCT CategoryID
FROM Products;
-- 오류 발생
-- DISTINCT는 집계함수를 사용할 수 없어서 오류가 발생한다.

 

  • GROUP BY와 DISTINCT 함께 활용
SELECT
  Country,
  COUNT(DISTINCT CITY)
FROM Customers
GROUP BY Country;
-- 그룹으로 묶어줄때 집계함수 내부에 포함시켜 줄 수 있다.

 

 

마치며...

포스팅을 하다보니 너무 방대해서 포스팅을 나눠서 해야할 필요성을 느껴, 이만작성하고 다음 포스팅에서 상관 서브쿼리와 비상관 서브쿼리, JOIN 등에 대해서 포스팅을 마저 해야겠다.

 

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

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

[ MySQL ] 상관,비상관 서브쿼리와 JOIN  (0) 2023.02.02
[ 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
2023. 1. 24. 15:14

Nest.js

Nest.js는 Node.js기반의 웹  API 프레임워크로서 Express, Fastify 프레임워크를

래핑하여 동작한다. 기본적으로는 Exress를 제공한다.

 

Nest.js의 장점

Node.js는 사용하기에 있어 편리하고 뛰어난 확장성을 가지고 있다.
이러한 특성으로 SW의 품질이 일정하지않고 라이브러리를 찾기위해
사용자가 많은 시간을 할애해야 한다. 반면 Nest.js는 데이터베이스,
ORM, Configuration, 유효성 검사 등 수많은 기능을 기본으로 제공하고 있다.
그러면서도 필요한 라이브러리를 쉽게 설치가능하기 떄문에

Node.js의 장점도그대로 가지고 있다.

 

 

Nest.js의 특징

Angular로부터 영향을 받아 모듈,컴포넌트 기반으로 프로그램을 작성함으로서

재사용성을 높인다. IoC(Inversion of Control), DI(Dependency Injection),

AOP(Aspect Oriented Programming)와 같은 객체지향 개념을 도입했다.

언어는 타입스크립트를 기본으로 채택하고 있다.

 

사용예시

Nest.js Install

npm i -g @nestjs/cli
//인스톨이 잘 되었는지 터미널에 nest를 입력해서 확인해본다.

인스톨이 잘 된 경우 터미널에 nest를 입력시 nest의 리스트가 나오게 된다.

 

nest new Project
//Project이름을 가진 Default create-nest가 생성된다.

프로젝트를 새로 만들어야 할 경우 Nest.js의 createt기능을 사용하여

Default프로젝트를 만들 수 있다.

 

 

src 폴더 구조

main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      forbidNonWhitelisted: true,
      transform: true,
      //transform은 요청데이터를 필요한 데이터형태로 바꿔준다. 원래는 string으로 받지만 number로 바꿔주고 있다.
    }),
  );
  await app.listen(3000); //run start시에 사용되는 서버포트이다.
}
bootstrap();

 

app.modules.ts

import { Module } from '@nestjs/common';
import { MoviesModule } from './movies/movies.module';
import { AppController } from './app.controller';

@Module({
  imports: [MoviesModule],
  controllers: [AppController],
  providers: [],
})
export class AppModule {}

 

app.controller.ts

import { Controller, Get } from '@nestjs/common';

@Controller('')
export class AppController {
  @Get()
  home() {
    return 'Welcome to my Movie API';
  }
}

src폴더는 Nest.js에서 root가 되는 구조이며 service.ts파일은 현재 사용예시 구조상 필요하지않고

movies폴더 내부에서 실질적인 구현을 대신하고 있으므로  movies폴더 내부에 service.ts파일이 있다.

 

 

movies 폴더구조

dto

dto는 코드를 간결하게 만들어 줄 수 있고 클라이언트로 부터 request를 받을때

유효성 검사를 해주기 위함이다.

 

create-movie.dto.ts

import { IsNumber, IsOptional, IsString } from 'class-validator';

export class CreateMovieDTO {
  @IsString()
  readonly title: string;

  @IsNumber()
  readonly year: number;

  @IsOptional()
  @IsString({ each: true })
  readonly genres: string[];
}
//DTO를 만드는이유는 코드를 간결하게 해주고 NestJS가 들어오는 쿼리에 대해 유효성을 검사할 수 있게 도와준다.

 

update-movie.dto.ts

import { PartialType } from '@nestjs/mapped-types';
import { CreateMovieDTO } from './create-movie.dto';

export class UpdateMovieDTO extends PartialType(CreateMovieDTO) {}

 

entities

entities는 DataBase에 넣어 줄 스키마를 미리 지정해주는 역할을 한다.

현재 예시는 간단하게 클래스를 지정하여 만들어 주었지만 실제 서비스에서는

typeORM을 사용하여 직접적으로 DB에 스키마를 만들어 줄 수 있으며

이 외에도 Prisma, Sequelize ORM 등이 있다.

movie.entity.ts

export class Movie {
  id: number;
  title: string;
  year: number;
  genres: string[];
}

 

movies 내부

movies.controller.ts

import {
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Patch,
  Body,
} from '@nestjs/common';
import { MoviesService } from './movies.service';
import { Movie } from './entities/movie.entity';
import { CreateMovieDTO } from './dto/create-movie.dto';
import { UpdateMovieDTO } from './dto/update-movie.dto';

@Controller('movies')
export class MoviesController {
  constructor(private readonly moviesService: MoviesService) {}
  /* movies.module.ts에서 @Module을 사용해서 controllers와 providers에서 import시켜주었기 때문에
  constructor(private readonly moviesService: MoviesService)와 같이 타입을 추가하는 것만으로
  같이 사용할 수 있게 되었다. 이것을 dependency injection이라고 한다.
   */

  @Get()
  getAll(): Movie[] {
    return this.moviesService.getAll();
  }

  @Get(':id')
  getOne(@Param('id') movieId: number): Movie {
    //필요한 것이 있을땐 반드시 요청을 해야한다. @Param()을 통해서 요청을 하고 props로 받는 구조다.
    console.log(typeof movieId);
    return this.moviesService.getOne(movieId);
  }

  @Post()
  create(@Body() movieData: CreateMovieDTO) {
    //@Body를 사용해서 클라이언트에서 보낸 object형식의 JSON을 추가할 수 있다.
    return this.moviesService.create(movieData);
  }

  @Delete(':id')
  remove(@Param('id') movieId: number) {
    return this.moviesService.deleteOne(movieId);
  }

  @Patch(':id') //데코레이터 Put과 Patch두가지가 있는데 Put은 모든 리소스를 받아오기 때문에 적합하지 않을 수 있다.
  patch(@Param('id') movieId: number, @Body() updateData: UpdateMovieDTO) {
    return this.moviesService.update(movieId, updateData);
  }

 

movies.module.ts

import { Module } from '@nestjs/common';
import { MoviesController } from './movies.controller';
import { MoviesService } from './movies.service';

@Module({
  controllers: [MoviesController],
  providers: [MoviesService],
})
export class MoviesModule {}

 

movies.service.ts

import { Injectable, NotFoundException } from '@nestjs/common';
import { Movie } from './entities/movie.entity';
import { CreateMovieDTO } from './dto/create-movie.dto';
import { UpdateMovieDTO } from './dto/update-movie.dto';

@Injectable()
export class MoviesService {
  private movies: Movie[] = [];

  getAll(): Movie[] {
    return this.movies;
  }
  //가짜 데이터베이스이며, 진짜데이터베이스에서는 Query를 가져온다.

  getOne(id: number): Movie {
    const movie = this.movies.find((movie) => movie.id === id);
    if (!movie) {
      throw new NotFoundException(`Movie with ID: ${id} not found.`);
      //HttpException에서 확장된 NestJS의 제공기능. 예외처리 기능을 한다.
    }
    return movie;
  }

  deleteOne(id: number) {
    this.getOne(id);
    this.movies = this.movies.filter((movie) => movie.id !== id);
  }

  create(movieData: CreateMovieDTO) {
    this.movies.push({
      id: this.movies.length + 1,
      ...movieData,
    });
  }

  update(id: number, updateData: UpdateMovieDTO) {
    const movie = this.getOne(id);
    this.deleteOne(id);
    this.movies.push({ ...movie, ...updateData });
  }
}

service.ts에서의 역할은 주로 서버에서 필요한 function의 집합체라고 이해할 수 있다.

가장 상위에서 @Injectable 데코레이터를 통해서 module내부에 provider로서

기능들을 주입할 수 있다.

 

 

마치며...

아키텍쳐를 정해두고 블록처럼 쌓아가는 형식으로 만들 수 있는것은 협업을 했을땐 오히려 가독성이 좋고 오차를 줄일 수 있는 장점이 있을 것 같다. 또한 프로그래밍의 방식을 고민해야할 것들이 이미 가이드라인으로 잡혀있다는 것 자체도 협업을 하는 시점에서 오해의 여지가 줄어들어 작업 효율이 좋아질 것 같아 보였다.

 

https://github.com/zeriong/Nest_intro

2023. 1. 14. 16:02

Next.js 파일(pages) 구조

public 						
├── asset     
      ├── image
      └── video

// 어플리케이션의 기능에 사용되는 소스들
component                                			 
├── common              
└── layout              
      ├── layout.tsx
      ├── header.tsx
      ├── nav.tsx
      └── footer.tsx
		                  
pages			
│                  
├── index.tsx
├── _app.tsx         
│                             
├── _document.tsx
│                      
├── api               
└── product
       └── [id].tsx

styles
├── globals.css             
└── Home.modules.css

pages 내부의 파일명에 따라서 해당 컴포넌트의 path가 정해진다.

pages내부에 index.tsx는 path = " / " 가장 첫화면(home)이고
about.tsx는 path = " /about " 이다.

 

pages => product => [id].tsx 의 경우

path = ' /product/id ' 가 된다.

 

 

_app.js  _document.js

  • 최초로 실행되는 파일들이다
    ( _app.js 는 최초 실행   =>   _document.js 는 _app.js이후실행 )
  • pages 폴더 내에 위치해야 한다.
  • 두 파일 모두 client 에서 사용하는 로직을 적용할 수 없다 -> eventListner 등
    < _app.js >
    전체 컴포넌트의 레이아웃으로 React.js에서 App.js와 같은 역할이다.
    최초에 실행되어 내부에 들어갈 컴포넌트들을
    전부 실행하고 Html의 Body로 구성하여 rendering한다.

 

 

404 page custom

pages 폴더에 404.js를 생성하고 그 곳에 원하는 커스텀을 해주면 된다.

 

사용예시

export default function NotFound() {
return "What are you doing here?"
}

 

 

 

rewrites기능

rewtrites는 말 그대로 다시 쓰는 기능으로, url에서 바뀐내용이 직접적으로 보여지는 것이 아닌url이나 console, network에서 볼 수 없도록 내부적으로만 바뀐 데이터를 받는 기능이다.

 

사용예시

/** @type {import('next').NextConfig} */
const API_KEY = process.env.API_KEY;

const nextConfig = {
  reactStrictMode: false,
  async rewrites(){
    return[
        {
          source: "/api/movies", //변경된 url
          destination: `https://api.themoviedb.org/3/movie/popular?api_key=${API_KEY}` //실행되는 파라미터
        },
      {
        source: "/api/movies/:id",
        destination: `https://api.themoviedb.org/3/movie/:id?api_key=${API_KEY}`
      }
        ];
  }
}

module.exports = nextConfig

 

해당 클라이언트 url에 source에 적힌 파라미터를 넣으면 destination에 할당된 내용으로 보여지지만

url에서 보여지는 파라미터는 source에 적힌 파라미터만 보여지게된다.

 

즉 url에 공유가 되면 안될 내용이 포함되어 있는 경우 rewrite를 사용하여

destination내부의 작성하면 된다. 

 

 

redirect기능

url에서 특정한 파라미터로 접속할 경우 따로 지정해둔 페이지로 연동시켜주는 기능이다.

 

사용예시

/** @type {import('next').NextConfig} */
const API_KEY = process.env.API_KEY;

const nextConfig = {
  reactStrictMode: false,
  async redirects(){
    return [
      {
        source:"/contact/:path*", // :path* 는 /contact/123456 처럼 아무거나써도 해당된다.
        destination:"https://www.google.com/", // 이동될 페이지
        permanent:false,
      }
    ];
  },
}

module.exports = nextConfig

 

마치며...

가독성이 좋은 아키텍쳐를 사용하는 것은 정말 좋다고 생각한다. 하지만 next.js의 서버부담이 크다는 단점은 seo가 필요한 서비스가 아닌경우에는 피하는게 좋다고 생각이 들었다.

 

github : https://github.com/zeriong/Nextjs_intro

2023. 1. 2. 20:57

1. Framework & Library

React.js는 javascript언어만으로도 html의 기능을 사용 가능하도록 만들고

반복되는 컴포넌트를 더 용이하게 사용할수 있도록 만든 Library이다.

그리고 Next.js는 React.js의 framework이다.

 

둘의 가장 명확한 차이점은 응용프로그램흐름의 주도권자 이다.

 

Framework

코드를 작성하는 기본적인 틀을 제공해서 보다 효율적으로 어플리케이션을 만들 수 있도록

소프트웨어 환경을 만들고 응용 프로그램은 프레임워크에 의해 사용된다.

 

Library

어플리케이션을 만들 때 필요한 자원 즉, 매소드(함수)의 모임이며

응용 프로그램이 라이브러리를 사용한다.

 

2. CSR & SSR

React.js는 CSR(Client-Side-Rendering)이다.

CSR은 클라이언트(Front)에서 브라우저에 보여지는 화면을 구성한다.

 

< 장점 >

초기에 로드만 완료되면 이후 렌더링이 빠르고 서버에 요청할 것이 적기때문에

서버의 부담이 적다.

 

< 단점 >

초기 html이 비어있기 때문에 SEO에 좋지않고 초기로드가 오래걸리고

외부 라이브러리가 추가로 필요한 경우도 많다.

 

장단점에 부합하는 " Web Applications "에 적합하다.

 

 

Next.js는 SSR(Server-Side-Rendering)이다.

SSR은 서버(Back)에서 브라우저에 보여지는 화면을 구성한다.

 

< 장점 >

초기 html에 모든 정보가 포함되어 있기 때문에 SEO에 좋고 초기로딩이 빠르다.

 

< 단점 >

서버에서 앱 전체를 미리 렌더링하기 때문에 컴포넌트 로딩이 오래걸리면 유저는

빈 화면을 보게 될 수도 있고 매번 서버에 요청하기 때문에 서버부하가 크다.

페이지를 요청할 때마다 새로고침되어 UX가 다소 떨어지는 편이다.

 

장단점에 부합하는 " Static sites "에 적합하다.

2022. 12. 26. 23:11

Redux-Toolkit

기존 리덕의 경우 reducer를 구현할 때마다 type 이름을 명확하게 적어야 하며

switch문이나 if/else를 사용해야하는 boilerpate코드가 많다.

간단하게 상태관리를 하겠다고 만든 리덕스이지만 많은 개발자들이 오히려 복잡한 코드를

짜야한 다는 것에 불만을 품었고 그 의견을 반영하여 redux팀 자체에서

redux-toolkit이 redux를 대체할 라이브러리라고 소개했고 실제로 더 간단하게

상태관리를 할 수 있는 라이브러리로 출시되었다.

 

상태관리 라이브러리의 원리

< 기존의 상태관리 방식의 시각화 >

React에서 State의 특성상 계속해서 props를 통해서 병렬적으로

상태를 관리할 수 밖에 없는 구조이다.

이렇게되면 로직처리중 의도치않은 버그가 발생할 수도 있고

아주 비효율적인 처리방식이라고 볼 수 있다.

 

 

< Redux의 상태관리 방식의 시각화 >

Redux는 Reducer와 Store를 통해서 상태변경과정을 간소화하고 직렬적으로

필요한 상태만 변경할 수 있게 만들어주기 때문에 효율적이고 간편하며

버그의 빈도를 낮추는 역할을 하게된다.

 

 

Redux 기본용어

Store

스토어는 컴포넌트의 상태를 관리하는 저장소다. 하나의 프로젝트 내에는 반드시 하나만 존재해야 한다.

Action

스토어의 상태를 변경하기 위해서는, 액션을 생성해야한다. 액션은 객체이며, 반드시 type을 가져야 한며 액션 객체는 액션생성함수에 의해서 만들어지고 type내부에 payload를 생성하고 state를 저장한다.

Reducer

리듀서는 현재 상태와 액션 객체를 받아 새로운 상태를 리턴하는 함수다.

Dispatch

디스패치는 스토어의 내장 함수 중 하나이며, 액션 객체를 넘겨줘 상태를 업데이트 시켜주는 역할을 한다.

Subscribe

스토어의 내장 함수 중 하나로, 리듀서가 호출될 때 서브스크라이브된 함수 및 객체를 호출한다.

 

 

 

Redux는 어떻게 상태를 변경할까?

 

1. UI가 처음 렌더링 될 때 UI는 스토어를 통해서 상태로 접근하여 초기에 설정해둔 값(initialState)으로 상태를 렌더링한다.

 

2. UI에서 상태가 변경되면 디스패치를 실행하여 action을 일으키고 type내부의 payload를 만든 후 전달받은 값을 가지고 스토어로가서 변경된 State를 저장한다.

 

4. 해당 UI는 Subscribe되었기 때문에 State가 변경된 것을 감지하고 변경된 상태를 렌더링한다.

 

 

사용예시

Redux-Toolkit Install

npm install @reduxjs/toolkit
//리액트 환경인 경우 react-redux 설치가 필요하다.

 

user.js   < reducer > 

import { createSlice } from '@reduxjs/toolkit';

export const userSlice = createSlice({
    name: "user",
    initialState: { value: {name: "", age: 0, email: ""}},
    reducers: {
        login: (state, action) => {
            state.value = action.payload
        },
    },
});

export const { login } = userSlice.reducer;

export default userSlice.reducer;

createSlice는 기존에 createReducer와 createAction 이 하던 일을 같이 해준다. 쉽게 말해 actions를 위한 js파일을 따로 만들 필요가 없다. 리듀서를 만들면 스토어에 저장을 해주어야한다.

 

 

store.js

import { configureStore } from '@reduxjs/toolkit'
import userReducer from './user'

export default configureStore({
    reducer:{
        user: userReducer
    }
})

redux-toolkit에서는 자동적으로 name + Reducer로 지정되어서 name: nameReducer가 된다.

 

 

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import store from './redux/store';
import { Provider } from 'react-redux';

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

Provider를 통해서 store를 전역으로 사용할 수 있도록 만든다.

 

 

Profile.js

import React from 'react'
import { useSelector } from 'react-redux'

function Profile() {
    const user = useSelector((state) => state.user.value);
    return (
        <div>
            <h1>Profile Page</h1>
            <p> Name : {user.name} </p>
            <p> Age : {user.age} </p>
            <p> Email : {user.email} </p>
        </div>
    );
}

export default Profile

 

Login.js

import React from 'react';
import { useDispatch } from 'react-redux';
import { login } from '../redux/user';

function Login() {
  const dispatch = useDispatch()
  return (
    <div>
      <button onClick={() => {
        dispatch(login({name: "내 이름", age: 20, email: "email@gmail.com"}))
      }}>Login</button>
    </div>
  );
}

export default Login

 

  • useSelector()로 스토어에서 현재 상태 값을 가져온다.
  • useDispatch()를 통해 변경되는 값을 스토어로 전달한다.

'Frontend > Redux' 카테고리의 다른 글

[ Redux ] 상태관리 라이브러리  (0) 2022.11.19