주의해서
데이터 무결성
참조무결성
dbms에서 2개의 관련있던 관계 변수(테이블) 간의 일관성(데이터 무결성)을 말한다.
CASCADE
SET NULL 옵션
임시테이블
테이블 이름 앞에 #이나 ##을 붙인다.
세션 종료시 제거
지역임시테이블 #
해당 임시 테이블을 만든 세션에서만 사용가능
전역임시테이블 ##
해당 임시 테이블을 만든 세션은 물론 다른 세션에서도 조회가 가능한 임시테이블
정규화 Normalization
데이터를 보다 효율적으로 저장하고
데이터 중복을 방지하고
저장공간이 낭비되는 테이블 설계를 제거하기 위함이다.
정규화를 진행할수록 테이블 개수가 많아진다.
그러나 정규화를 통해 얻는 이점도 많아진다.
1차 정규화
반복되는 속성이나 그룹의 속성을 제거하고
새로운 테이블을 추가한 후에 기존 테이블과 일대다 관계를 형성하는 것
2차 정규화
기본키가 복합키로 구성되었을 때, 복합 키 전체에 의존하지 않고 일부에 의존적인 열이 있으면 이를 제거해야 하는 것
3차 정규화
기본 키에 의존하지 않고 일반 열에 의존하는 열이 있다면 이를 제거해야 하는 것을 의미
INNER 조인 처리
논리처리 단계를 카티전 곱으로 처리한다.
카티전 곱
두개 이상의 테이블을 조인할 때, 조건을 지정하지 않고 모든 행을 연결하여 결과를 생성하는 연산
카티전 곱의 결과는 중복된 행이 많이 포함될 수 있다.
일반적으로 카티전 곱은 성능문제를 유발할 수 있으므로, 조인 조건을 지정하여 필요한 행만 선택하는 것이 좋다.
내부 조인의 과정을 한번 살펴보자
1) SELECT emp.FirstName, ord.Amount
2) FROM HR.Employee AS emp
3) JOIN Sales.SalesOrder AS ord
4) ON emp.EmployeeID = ord.EmployeeID;
라고 했을 때 가장 먼저 from이 수행되고
그 다음에 join이 수행된다. 여기서 카티전 곱으로 경우의 수가 모두 발생하고 거기서 이제 ON으로 필터링을 거친다.
필터링을 거친 이후에 맨 마지막으로 SELECT 문이 수행된다.
와일드카드
MYSQL과 같음
LIKE %, _
내부조인의 실행순서 한번 다시보기
SELECT 이름, 내용 등등....
FROM 테이블 별칭1
INNER JOIN 테이블 별칭 2
ON 어떻게 조인?
WHERE ~랑 ~랑 같다는 조건~~서브쿼리 있음
외부조인
외부조인은 셋으로 나뉨. 왼/풀/오 그런데 왼/오 중 하나만 할 줄 알면 된다. 위치를 바꾸면 되니까
크로스 조인
크로스 조인은 조인 조건이 필요없다.
크로스 조인은 테이블 간의 카티전 곱의 데이터를 갖는다.
SQL의 실행순서
FROM을 먼저, JOIN이 있다면 가상 테이블에 JOIN으로 합쳐진 데이터를 결합
FROM으로 가져온 데이터를 갖다가 WHERE의 조건에 따라서 필터링 한다.
GROUP BY, 이전의 단계를 거쳐서 추출된 데이터들을 GROUP BY에 명시된 컬럼을 기준으로 그룹핑한다.
HAVING 그룹핑된 데이터에 대해서 HAVING에 명시된 필터링을 실행한다
여기서 WHERE와 헷갈릴 수 있는데, HAVING은 그룹핑된 데이터에 필터링을 거는거다.
그래서 GROUP BY가 없으면 애초에 사용할 수 없고,
만약에 WHERE절에 걸 수 있는 필터라면 HAVING보다 WHERE에 거는 것이 바람직하다.
그리고 SELECT로 원하는 컬럼을 선택하고
ORDER BY로 추출하면 댐
TOP이나 이런건 뭐 알아서
WHERE 절의 성능을 최적화하기 위해서는
INDEX를 타는지 항상 체크해라
쿼리 작성시에 반드시 필요한 컬럼만 명시해라
WHERE 조건문의 왼쪽은 순수한 컬럼만 넣어라
조인을 사용하면 되도록이면 내부조인을 써라
서부 쿼리 사용시에 불필요한 SELECT 구문을 줄여라
궁금한게 생겼었다. 필터링의 시간복잡도는 어떻게 될까. SQL의 필터링은 내부적으로 어떻게 구현이 되어있고 무엇에 영향을 받을까?
어쩔 수 없이 모든 행을 살펴보려면 시간복잡도는 O(N)이 되어야 한다고 생각했으나..분할정복으로 하면 대략 O(LOGN)~ < O(N)
그런데 CLOVAX한테 물어보니까 병렬처리가 되어있다고 한다.
병렬처리는 참고로 여러개의 CPU 코어를 사용해서 동시에 여러 작업을 수행하는 기술
그렇다면 SQL의 성능은 CPU 코어 개수와 CPU의 성능에 좌우된다는 소리이다.
SQL 안에서 WHERE는 어떻게 구현되어 있길래 병렬처리를 쓴다고 하는걸까
WHERE절의 조건에 따라서 데이터베이스 테이블을 여러 개의 파티션으로 분할한다. 각 파티션은 독립적으로 처리됌...!
분할정복
병렬처리 알고리즘
분할된 파티션을 여러개의 CPU코어가 동시에 처리
병렬처리 알고리즘
그렇기 때문에 CPU속도와 코어 개수에 많이 의존하게 된다. 결론적으로 컴퓨터 성능에 의존한다는 소리.
CASE 문은 항상 문자열을 반환한다.
COUNT는 정수형을 반환한다.
CASE( ~~ AS 형)으로 데이터 타입을 변환할 수 있다.
ISNULL( 1, 2 )
1이 널이 아니면 2로 대치
특정 부분만 ~로 간주해서 정렬하기
예를 들어서 부서 아이디 DeptID가 STG라는 부서가 있다고 하자.
이 부서를 맨 위에 정렬하고, 나머지는 그대로 하고 싶다. 현재 오름차순으로 정렬되어있다고 하자.
그럼 이 STG를 AAA로 간주한다.
ORDER BY CASE DeptID WHEN ‘STG’ THEN ‘AAA’ ELSE DeptID END ASC;
CASE문은 여러모로 쓸 일이 많다.
트러블 슈팅 1
어떤 상황이냐면, 서로 외래키가 서로의 주키를 참조하고 있는 상황
NULL 허용
애초에 처음부터 NULL을 허용하도록 변경한다
..? 이게 맞나
데이터 모델 재설계
애초에 이렇게 양방향 종속성이 생기는 것이 안 좋다.
정규화는 연습이 피료해....
내가 미쳤나봄
애초에 말이 안되게 생김. 테이블 ERD 짤때도 정규화가 필요한 것 같다.
(졸면서 짜면 이런 실수 하는거)
데이터를 가장 빨리 찾는 방법은
인덱스
클러스터
클러스터형
클러스터 형 인덱스는 테이블이다.
힙형 테이블을 기준열로 정렬하면 클러스터형 테이블이 된다.
클러스터형 테이블에서 데이터는 클러스터형 인덱스에 의해 정렬되어 저장된다.
클러스터형 인덱스는 테이블의 데이터를 물리적으로 순서대로 저장한다.
이 인덱스는 테이블의 데이터 행 자체를 포함한다.
클러스터형 인덱스는 테이블 당 하나만 생성할 수 있다.
이 인덱스에 정의된 열(들)을 기준으로 저장된다.
따라서 힙형 테이블에 클러스터형 인덱스를 추가하면, MSQL은 데이터를 인덱스의 키 값에 따라 물리적으로 재정렬 한다.
이후에 테이블은 클러스터형 테이블이 되며, 클러스터형 인덱스의 주요 이점은 데이터 검색과 정렬이 보다 효율적이고
특히 범위 검색이나 순차 접근이 많은 경우에 유리하다.
클러스터형 인덱스 스캔
무조건적으로 나쁘다고 할 수는 없으나 성능저하의 원인이 되기도 한다.
원하는 열로 정렬된 클러스터형 인덱스에서 찾으려고 하는데 내가 찾으려고 하는 것이 다른 열을 기준으로 된 것이라면 테이블 전체를 스캔할 수 밖에 없다.
이것을 클러스터형 인덱스 스캔이라고 한다.
데이터를 전부 가져오는 제일 빠른 방법이 테이블 스캔이기도 하다.
비클러스터형
데이터를 정렬하지 않고 테이블 스캔을 피하려면?
INDEX SEEK (비클러스터형 인덱스)
root – intermediate – leaf 구조로 찾는건데 어떤식으로 찾느냐면
맨 위의 루트 레벨에서 다음과 같은 식으로 저장된다. ( 맨 끝 페이지 )
(~~~, 200 )
( 26 , 201 )
그렇다면 여기서 알 수 있는 것이 15는 26보다 작으니까 200번 페이지에 있음을 확인할 수 있다.
이후에 200번 페이지로 가게 되면, 고객번호 (기준열)와 함께 RID 가 저장되어있는데
RID는 다음과 같이 생겼다. ~~ : 페이지번호 : 몇번째 줄 ?
그럼 그 다음엔 그거 보고 그 페이지 가서 찾으면 된다..!
이걸 index seek이라고 한다.
RID LOOKUP
일단 데이터 페이지랑 인덱스 페이지를 구분해서 생각하자.
요약하면, 비클러스터형 인덱스에서 힙으로 데이터 행을 찾아가는 과정이다.
PAGE SPLIT
데이터가 추가되었는데 리프 페이지가 꽉 차 있다면 넣을 수가 없다.
때문에, 절반 정도를 새로운 페이지를 만들어서 그 페이지로 넘기고
그 다음에 새로운 데이터의 인덱스를 추가한다.
힙은 페이지 분할이 일어날 일이 없다.
그냥 빈 곳에 쑤셔 넣고, 빈 곳이 없으면 뒤에 낑겨 넣는다.
맨 위의 루트레벨의 인덱스에서는
어떤 리프페이지의 첫 데이터가 기준열의 몇번째 데이터부터 시작하는지를 다룬다.
복잡하지?
그래서 비 클러스터형 인덱스에서는 SELECT문은 큰 효과를 볼 수 있지만, INSERT문은 인덱스에 등록하는 과정이 꽤 복잡하다.
클러스터형 인덱스의 리프페이지는 데이터페이지다.
비클러스터형 인덱스는 리프페이지와 데이터페이지가 따로 있었으나
클러스터형은 리프페이지와 데이터페이지가 같다. 실제로 리프페이지 안에 물리적으로 정렬되어서 저장되어있다.
내용 정리해보자
클러스터형 인덱스와 비클러스터 인덱스를 좀 팠는데,
크게는
힙
그냥 막 섞인..
중간에 빈 곳이 있으면 낑겨서 넣는데 빈 곳이 없다면 페이지를 새로 생성해서 뒤에다가 낑겨서 넣는다.
클러스터형 인덱스
데이터페이지는 정렬되어있다.
정렬되어있어서 찾기는 좋은데, 어떤 페이지를 먼저보아야 할지를 모른다.
때문에 인덱스 키와 페이지 번호가 적혀있는 인덱스가 필요하다!
RID는 필요가 없다.
비클러스터형보다 단계가 하나가 없다보니까, 더 나은 것 같지만 insert 하기 힘든 것은 솔직히 비슷하다.
비클러스터형 인덱스
비클러스터형 인덱스 + 클러스터형 인덱스
클러스터형의 데이터페이지는 물리적으로 정렬된 건 아니다.
페이지의 슬롯인덱서가 논리적 순서를 매핑한다
클러스터형 + 비클러스터형
클러스터형 인덱스라도 결국에 인덱스테이블이 많아지게 되면, 그것또한 누가 어디에 있는지 알려줘야 한다.
(정렬해놓긴했지만.. 일단 어딘지 알아야 .. ㅇㅇ )
그래서 비클러스터형 인덱스를 위에다가 따로 구성한다
페이지 분할을 방지하려면
일단 힙에서는 페이지 분할이라는게 있을 수 없음
청소 안하는 사람이 책장정리할 필요가 없음. 그냥 던져놓으면 됌
페이지 분할을 방지하기 위해서 원인을 원천 차단한다!
페이지의 전체를 다 채우지 않고 50%만 채워놓는다고 쳐보자
그러면 페이지가 2배로 더 필요하다.
어떻게 보면 SELECT 문이 읽어야할 페이지가 늘어나는 거긴한데,
그리고 페이지 분할이 안일어나잖아..ㅇㅇ
밀당인거임..
주키(PRIMARY KEY)는 기본적으로 클러스터형 인덱스로 생성된다.
이후 SQL SERVER는 해당 열을 기준으로 데이터를 물리적으로 정렬하는 클러스터형 인덱스를 자동으로 생성하고
그러면 검색 속도가 빨라짐.
주키를 비클러스터형으로도 만들 수 있다.
뷰
데이터베이스 복잡성을 숨길 수 있음
성능향상
인덱스된 뷰를 만들지 않는 이상은 성능상 이점이 없음
보통은 단지 권한을 효과적으로 관리하고
원하는 데이터에 쉽게 접근하기 위해서 씀
필요한 데이터만 보여주니, 정보유출 위험을 줄일 수 있다.
뷰를 만들때 원칙이 몇가지 있다.
32중 이상으로 중첩은 불가능
열은 1024개부터 불가능
COMPUTE, COMPUTE BY, COMPUTE INTO는 사용할 수 없음
TOP 없이 ORDER BY를 사용할 수 없음
뷰 임시테이블
정의 하나 이상의 테이블에 대한 쿼리의 결과로 생성되는 가상 테이블이다.
실제로 데이터를 저장하지 않고, 정의된 쿼리에 따라서 데이터를 동적으로 표시한다. 세션 또는 트랜잭션 동안에만 존재하는 테이블이다.
실제로 데이터를 저장하며, 작업 중간 결과를 저장하는데 사용된다.
저장 데이터베이스 내에 정의가 저장되며,
뷰를 참조할 때마다 기본 테이블의 데이터를 쿼리한다. 메모리나 디스크에 데이터를 실제로 저장한다.
데이터는 세션이나 트랜잭션이 종료될 때까지 유지된다.
용도 데이터의 재사용성과 보안, 복잡한 쿼리의 간소화를 위해서 사용된다.
또한 사용자에게 특정 데이터만 보여주고자 할 때 유용하다. 복잡한 처리를 위한 중간단계
대량의 데이터를 임시로 처리하거나, 변환하는 경우
반복적인 데이터베이스 작업을 최적화하기위해 사용된다.
수명 사용자가 삭제하거나 데이터베이스를 삭제할 때까지 데이터베이스 세션이나 트랜잭션이 종료되면 자동으로 사라진다.
뷰 관리
ALTER VIEW 문으로 뷰에 포함된 SELECT 문을 변경할 수 있다.
뷰를 DROP문으로 제거한 다음 CREATE VIEW로 다시 만들면, 같은 결과가 아니다.
DROP VIEW 문으로 뷰를 제거하면 뷰에 대한 데이터베이스 사용자 권한도 같이 제거된다.
VIEW문으로 뷰를 다시 만들어도 제거된 권한은 복원되지 않는다.
그러니까 꼭 뷰 변경은 DROP하고 다시 만들지 말고
ALTER VIEW를 쓰자.
뷰
유효기간
뷰는 특별한 조건이나 시간 제한 없이 유효하다.
DROP VIEW 문으로 삭제 가능하나 만약에 그게 변경을 하려는 경우라면 추천하지 않는다.
권한까지 같이 삭제되기 때문
뷰의 기반이 되는 테이블이 변경되면
오류가 발생할 수 있고, 더이상 유효하지 않을 수 있다.
뷰의 권한 변경
뷰에 대한 액세스 권한이 변경될 경우에 뷰 권한은 여전히 존재하지만
특정 사용자가 뷰를 사용할 수 없게 될 수 있다.
프로시저
리스트 몇개를 외워야 할듯?
sp_helptext : 구문확인
구문 암호화
WITH ENCRYPTION
근데 암호화는 왠만하면 하지말라..
하게 된다면 구문을 따로 저장해야 한다.
나중에 뷰 수정하려면..
구문을 나중에 따로 볼 수 없으
뷰가 참조하는 개체를 보호
WITH SCHEMABINDING
주로 뷰나 사용자 정의함수의 정의에 사용된다.
해당 객체가 참조하는 테이블이나 뷰의 스키마가 변경되는 것을 방지한다.
데이터 무결성을 유지하는 데에 도움이 된다.
성능 최적화
WITH SCHEMABINDING을 사용하여 생성된 뷰는 인덱싱될 수 있어서, 성능이 향상될 수 있다.
이것은 뷰가 더 효율적인 실행 계획을 가질 수 있도록 도와준다.
변경제한
WITH SCHEMABINDING을 사용한 객체는 참조하는 테이블이나 다른 뷰가 변경되지 않는 한 수정이 제한된다.
스키마 변경 시 실수로 인한 오류를 방지할 수 있다.
저장 프로시저
SQL SERVER를 사용하면서 저장프로시저를 사용하지 않는 서비스는 없다
그만큼 중요
이점
캡슐화
성능
실행계획을 재사용하면 CPU를 절약할 수 있다.
SQL 자체가 CPU 병렬 알고리즘을 사용하기 때문에 CPU랑 이런식으로 연관이 되는 것 같다.
네트워크 트래픽 최소화
쿼리문의 문자열이 줄면 네트워크 트래픽도 감소하게 된다.
쿼리문은 긴 편인데, 저장 프로시저 이름과 매개변수만 사용하기 때문
보안계층으로 사용
객체에 접근할 권한을 직접 주지 않고, 저장 프로시저 실행권한만 부여하면 된다.
객체에 대한 불필요한 접근을 제한할 수 있다.
SQL의 실행을 한번 보면
SQL 같은 경우는 띄어쓰기 등 조금만 변경되어도 다른 쿼리로 인식하고 그렇기 때문에 다른 쿼리로 인식될 경우의 수가 커서 결국에 CPU를 많이 갈구게 되어있다.
결국 실행계획이 재사용되는 일은 거의 없어지고, 쿼리 컴파일이 빈번하게 일어난다.
CPU와 메모리 사용량이 올라가서 성능에 악영향을 끼친다.
그냥 저장 프로시저 쓰라는 말이다.
SET NOCOUNT ON
개의 행이 영향을 받았습니다 같은 메시지 안뜨게 한다
엥 근데 이것도 이점이 많네..
이점
메시지 억제
클라이언트에 ~~개의 행이 영향을 받았다라는 메시지를 반환하지 않음
대량의 데이터를 처리하는 쿼리나 프로시저에서 유용
네트워크 트래픽 감소
이런 메시지들을 억제하면 클라이언트와 서버간의 네트워크 트래픽을 줄일 수 있다.
그러면 네트워크 부하가 감소하고 이것은 전체적으로 전반적인 성능을 향상시키는 것에 도움을 준다.
성능향상
일부 어플리케이션은 그 메시지의 행수에 대한 처리에 부담을 느낄 수 있다고 한다
그거 없애면 성능에 도움이 될 수도 있다는..
간결성
쓸데 없는 메시지가 없으니 결과가 더 간결하다.
프로시저 디테일
매개변수에 DEFAULT로 전달하면 기본값으로 구문이 실행됨
물론 기본값을 사전에 설정해놔야 ( 프로시저 만들때나.. )
매개변수를 생략할 수도 있다.
단축형
EXECUTE 프로시저 명, 매개변수 순서대로 나열
근데 중간에 매개변수 1, ,매개변수 3
이런식으로 맘대로 하면 안돼
매개변수 1, DEFAULT, 매개변수 3으로 이런식으로 설정해야해..
프로시저에서 리턴값을 따로 설정하지 않았다면 default는 0이다.
프로시저는 리턴값을 받던 안받던 일단 리턴값을 무조건 넘긴다.
BEGIN
코드 블록의 시작
T-SQL에서 복잡한 실행 논리를 포함하는 코드 블록의 시작을 나타냄
특히 조건문이나 반복문 같은 제어문에서 유용
절차적 논리의 구성
저장 프로시저는 데이터베이스 작업을 수행하기 위해서 여러 SQL문장을 순차적으로 실행한다
그래서 BEGIN과 END를 잘 써서 이러한 문장들을 하나의 논리적 단위로 잘 구성해야한다
그래야 보기 깔끔하겠지?
가독성 및 유지관리
BEGIN을 사용함으로써 코드의 가독성이 향상되며 복잡한 프로시저 내에서 어떤 코드가 특정 논리적 단위에 속하는지 명확하고 빠르게 할 수 있다.
오류 처리 및 트랜잭션 관리
BEGIN을 통해서 정의된 블록 내에서 오류 처리 및 트랜잭션 관리를 보다 효과적으로 수행할 수 있다.
BEGIN TRANSACTION
BEGIN TRY
BEGIN CATCH
로 사용할 수 있다.
RAISEERROR X / RAISERROR O
시스템 함수 ( 시스템 전역변수 )
보통 시스템 전역 변수를 사용하는 사유는 SQL SERVER의 내부 상태를 모니터링하고, 스크립트 또는 저장 프로시저 내에서 조건부 논리를 구현하는데 유용하게 사용하기 위해서이다.
ALTER PROC dbo.usp_GetEmployee
@DeptID char(3) = NULL,
@FromDate date = '2024-01-30',
@ToDate date = '2024-01-31',
@RowNum int OUTPUT
AS
SET NOCOUNT ON
-- 부서코드가 NULL값이면 오류 메시지 표시 후 종료
IF @DeptID IS NULL
BEGIN
RAISERROR('부서코드가 NULL입니다.',10, 1)
RETURN -1
END
-- 정상적인 경우
SELECT e.EmpID, e.EmpName, e.Gender, e.DeptID, e.HireDate, e.Phone, e.EMail
FROM dbo.Employee e
WHERE e.DeptID = @DeptID
AND
e.HireDate BETWEEN @FromDate AND @ToDate
SET @RowNum = @@ROWCOUNT
RETURN 0
GO
DECLARE @Return int
DECLARE @Rows int
EXECUTE @Return = dbo.usp_GetEmployee
@DeptID = 'SYS',
@FromDate = '2014-01-01',
@ToDate = '2014-03-01',
@RowNum = @Rows OUTPUT
CREATE TABLE #Employee (
EmpID char(5) PRIMARY KEY,
EmpName nvarchar(4),
Gender char(1),
DeptID char(3),
HireDate date,
Phone char(13),
Email varchar(50)
)
-- 변수 선언
DECLARE @Return int
DECLARE @Rows int
-- 저장 프로시저 실행결과를 테이블에 기록
INSERT INTO #Employee EXEC @Return = dbo.usp_GetEmployee
@DeptID = 'SYS',
@FromDate = '2014-01-01',
@ToDate = '2014-12-31',
@RowNum = @Rows OUTPUT
SELECT e.EmpID, e.EmpName, e.HireDate, e.Email
FROM
#Employee e
GO
살짝 의아한건디 보면
@RowNum = @Rows OUTPUT을 보면
@RowNum은 프로시저에서 보내는 값임. 근데 그걸 @Rows로 반환하는 거다. 뒤에 붙은 OUTPUT키워드는 이 매개변수가 저장 프로시저의 외부로 값을 전달한다는 것을 나타낸다.
헷깔릴 수 있는게 원래 값의 대입은 오른쪽에서 왼쪽으로 가는게 일반적인데 이건 좀 흐름에서 벗어난 것 같긴하다.
SQL은 튜링완전언어는 아니라고 한다.
그래도 간단한 제어정도는 가능한 것 같다
EX) 저장 프로시저 : 널처리
과제
CREATE PROCEDURE dbo.usp_GetVacation
@EmployeeID char(5),
@StartDate date,
@EndDate date
AS
BEGIN
SELECT
e.EmpID AS 사원아이디,
e.EmpName AS 이름,
e.EngName AS 영문이름,
e.HireDate AS 입사일,
e.Phone AS 연락처,
v.BeginDate AS 휴가시작일,
v.EndDate AS 휴가종료일,
v.Reason AS 휴가사유
FROM
dbo.Employee AS e
INNER JOIN
dbo.Vacation AS v
ON
e.EmpID = v.EmpID
WHERE
e.EmpID = @EmployeeID
AND
v.BeginDate BETWEEN @StartDate AND @EndDate
ORDER BY
v.EndDate DESC
END
GO
EXECUTE dbo.usp_GetVacation 'S0001','2011-01-01', '2011-08-30'
GO
과제 체크사항 적혀있던거
데이터를 기록하는 저장 프로시저를 만드려면 대상 테이블의 구조를 정확히 알고 있어야 한다.
참고로 Vacation 테이블의 휴가번호(pk, identity)는 identity 속성 열이므로 값을 전달할 필요가 없음
Duration 또한 계산된 열이므로 역시 값을 전달할 필요가 없음
실행계획을 분석해보쟝 .. 어렵다..
일단 클러스터 인덱스 스캔이 엥 왜 6of 3?? 3of 6이 아니라 저건 무슨말이지..
아마 3 of 6이 맞는 표현 같은데 잘못 표시가 된 것 같다..
아닌데... 200%로 보니까 6of 3가 맞다.. 뭐지
일단 보류..
실행계획 재사용 및 재컴파일
“우리는 쿼리문에서 우리가 원하는 것이 무언인지를 정의하는 것이지 어떻게 가져올지 정의하는게 아니다”
“그건 SQL SERVER 가 결정한다”
SET STATISTICS IO ON
스캔수, 읽기 표시
사용자정의함수
뷰는 FROM 절에 오기 때문에
SELECT절과 WHERE절을 사용해서 원하는 데이터만 필터링해서 볼 수 있는 장점
매개변수가 없어서 다른 형태의 데이터를 원하면 그 형태를 보여주는 뷰를 다시 만들어야 하는 단점
SELECT문만 가질 수 있는 것도 뷰의 단점
저장 프로시저
매개 변수를 가지고 여러 유형의 쿼리문으로 온갖 처리를 할 수 있는 장점
FROM절에 사용할 수 없기 때문에 주는 결과를 그대로 받아야하는 단점
쿼리문의 일부 (SELECT / WHERE 절)로 사용할 수 없는 것도 단점
매개변수도 있고, 뷰처럼 FROM절에도 오고, 저장 프로시저처럼 다양한 쿼리 구문을 가지고 있으면서
쿼리문의 일부에도 사용할 수 있는
사용자정의함수
사용자 정의함수는 크게 3가지로 구문됨
스칼라
성능에 좋지 않다. WHERE 절 조건 검사마다 다시 실행된다.
스칼라 함수를 실행하는 쿼리의 시간복잡도는 못해도 O(N)이다.
DB는 계속해서 커지는 방향을 가지고 있을텐데 그러한 측면에서 보면 안좋다.
단일문 (인라인)
가장 쉬움
다중문
편해.. 이게 제일 쓰기가 좋은 것 같아..
INSTEAD OF 트리거
SQL SERVER에서 제공하는 특별한 유형의 트리거
기존의 데이터조작명령(DML) (INSERT, UPDATE, DELETE)을 대신하여 다른 작업을 수행하도록 설계되었음.
이 트리거는 특히 뷰에 대한 작업을 처리할때 유용하며, 테이블에도 사용될 수 있습니다.
INSTEAD OF 트리거의 기능
명령 대체 기능
이 트리거는 기본 DML명령이 실제로 실행되는 대신에, 트리거에 정의된 사용자 지정로직을 실행한다.
즉 트리거가 DML 명령의 실행을 ‘대신’한다.
복잡한 로직 구현을 하는데에 사용
복잡한 데이터 검증 , 다중 테이블 갱신 , 비즈니스 규칙의 적용 등 복잡한 로직을 구현하는데에 사용될 수 있음
뷰에 대한 작업처리
뷰에 INSERT나 UPDATE, DELETE 같은 작업을 수행할 때, 뷰가 여러 테이블로 구성된 경우 해당 작업을 처리하기 어려울 수 있 는데, INSTEAD OF 트리거는 이런 작업을 사용자 정의 방식으로 처리할 수 있게 해준다.
이제 DDL 트리거
자 GPT와 함께 알아보자.
DDL 트리거는 데이터 정의 언어 이벤트 ( Data Definition Language ) 예를 들어서 CREATE, ALTER, DROP 등등...
테이블, 뷰, 인덱스, 스키마 등 데이터베이스 객체의 생성 변경 삭제와 같은 작업을 수행할때 발생하는 이벤트에 반응하여 실행되는 특수한 유형의 SQL SERVER 트리거이다.
DDL 트리거는 데이터베이스 또는 서버 수준에서 정의할 수 있으며, 데이터베이스 스키마의 변경을 관리하고 감사하는데에 유용하게 사용된다.
주요기능을 살펴보자!
1. 변경관리
A. 데이터베이스 스키마 변경사항을 감시하고
B. 무단 변경을 방지한다.
C. 예시로 특정 테이블의 변경을 제한하거나, 스키마 변경 시에 자동으로 관리자에게 알림을 보낼 수 있다.
2. 감사로깅
A. 데이터베이스 객체에 대한 모든 변경 사항을 기록하여, 누가 언제 어떤 변경을 했는지 추적할 수 있다.
i. 이것은 데이터베이스의 무결성을 유지하는데에 중요한 역할을 한다.
B. 자동화작업
i. 특정 DDL 이벤트에 반응하여 자동으로 추가 작업을 수행하도록 설정할 수 있다.
1. 예시로, 테이블이 생성될 때 자동으로 관련된 보안 정책을 적용할 수 있다.
GPT가 알려주는 예시 쿼리문을 보자
CREATE TRIGGER 이름 쏼라
ON DATABASE -> DB수준
FOR CREATE_TABLE -> 이 DDL 이벤트에 대해서 ( 테이블 생성 )
AS
BEGIN
DECLARE @EventData XML = EVENTDATA()
INSERT INTO dbo.TableCreationAudit(ChangeDate, EventData)
VALUES ( GETDATE(), @EventData )
END
GO
(xml은 아직 안했는데.. ) 어쨋든
주의사항 한번 살펴보자
DDL (data definition language) 이벤트가 발생하면 시작되는 트리거
CREATE ALTER DROP.. ETC
데이터베이스 영역
데이터 베이스 레벨에서 체크하는거라고 보면 된다
예를 들어서, 맘대루 테이블을 삭제하려고 하거나 (예를 들어서 나같은 신입이 실수로 지울 수 있으니...)
변경하려고 할때 방지할 수 있다. 만드는건 요로코롬
CREATE TRIGGER 트리거 이름
ON DATABASE -> 이거 중요 이게 어떻게 보면 DDL트리거 - DB레벨 이라는걸 인지시켜주는 역할
FOR ALTER_TABLE, DROP_TABLE -> 이걸 하려고 하믄
AS
PRINT ‘’
ROLLBACK TRANSACTION
GO
한가지 알아야할 게 있는데, 외래키 제약에 대한 충돌이 DDL 트리거 (DB레벨) 보다 우선한다.
체크를 우선적으로 한다는겨 그래서 에러메시지가 생각과 달랐던 것이어따
서버 영역
ON ALL SERVER
트랜잭션
먼저 계속 말하더라. 데이터베이스의 일관성은 매우 중요하다. 항상 동기화, 락 이런 개념이 나오면 어려워지더라. 근데 그만큼 중요하니까..
드가보자..
먼저 트랜잭션부터 살펴보자.
트랜잭션은 디비에서 엄청 중요중요한 개념으로 하나의 논리적인 작업 단위를 말한다.
보통 ACID 특성이라고 하는데 이걸 잘 생각하면서 날려야 한다.
기억이 가물가물한데 ATOMY, CONSISTENCY, ISOLATION, DE... 기억안난다..
DURABILITY라고 한다 (역시 GPT)
한번 살펴보자.
1. 원자성
A. 트랜잭션 내의 모든 작업은 한번에 처리되어야 한다.
B. 모두 성공적으로 실행되거나 아니면 아예 100% 실행되지 말아야 한다.
2. 일관성
A. 트랜잭션이 성공적으로 완료되면, 데이터베이스는 하나의 일관된 상태에서 다른 일관된 상태로 전환되어야 한다.
3. 고립성
A. 동시에 실행되는 여러 트랜잭션들은 서로 영향을 주어서는 안된다.
B. 각 트랜잭션은 독립적으로 실행되어야 한다.
4. 지속성
A. 트랜잭션이 성공적으로 완료되면, 그 결과는 시슽엠의 장애가 발생하더라도 영구적으로 반영되어야 한다.
-- 데이터베이스 작업 수행
INSERT INTO myTable (Column1) VALUES ('Value1');
UPDATE myTable SET Column1 = 'Value2' WHERE Column2 = 'Condition';
DELETE FROM myTable WHERE Column3 = 'Condition';
-- 모든 작업이 성공적으로 완료되면 트랜잭션을 커밋합니다.
COMMIT TRANSACTION;
-- 만약 오류가 발생하면 트랜잭션을 롤백합니다.
ROLLBACK TRANSACTION;
다음으로 이런 경우도 가능해졌다.
BEGIN TRANSACTION
DELETE dbo.Employee
WHERE EmpID = ‘S0002’
ROLLBACK TRANSACTION
GO
아까는 비슷한 사례가 롤백이 안되었었는데 이제는 롤백 트랜잭션 ( 취소 ) 가 가능하다.
XACT ABORT 옵션에 대해서
명시적 트랜잭션에서 필수로 달고 있어야 하는 것 같다.
주로 DML문에서 많이 쓴다.
대부분의 오류가 트랜잭션을 자동으로 롤백하게 하지만, 모든 종류의 오류가 트랜잭션을 롤백하는 것은 아니다.
예를 들어서 구문오류 같은 일부 오류는 트랜잭션을 롤백하지 않는다.
트랜잭션 중에 발생하는 모든 오류가 트랜잭션을 자동으로 롤백하게 한다.
오류가 발생하면 SQL 서버가 현재 실행 중인 트랜잭션을 즉시 종료하고 이전 상태로 되돌린다.
중요성
오류 처리 간소화
이 옵션을 사용하면 복잡한 오류 처리 로직을 작성할 필요가 없다.
트랜잭션 중 발생하는 어떠한 오류도 자동으로 트랜잭션을 롤백하기 때문이다.
이것은 특히 또또또 데이터 무결성을 유지하는 것에 중요하다.
데이터 무결성 보장
쿼리 오류가 발생하면 모든 변경사항이 롤백되므로, 데이터 베이스는 일관된 상태를 유지한다.
트랜잭션이 발생하는 과정을 살펴보면,
먼저, SQL 서버는 트랜잭션 로그를 남긴다.
다음에 체크포인트마다 실제 데이터에 반영을 한다.
근데! 여기서 롤 포워드 / 롤 백 개념이 나오는데 함 보자
예를 들어서
트랜잭션이 일어났고, 체크포인트를 지나서 별 오류 없이 잘 끝났다.
그러면 체크포인트에서 저장된 이후의 내용을 트랜잭션 로그를 참고해서 마저 진행한다.
트랜잭션이 일어났고 체크포인트를 지났는데 갑자기 시스템 정지와 같은 상황이 벌어져서 잘 안끝났다.
그렇게 되면 체크포인트 이후의 반영은 안된 것이기 때문에, 트랜잭션 로그를 참고해서 다시 롤백한다.
지금 트랜잭션 모드가 3개 나왔자나
1. 자동 커밋 트랜잭션 ( default )
A. SQL SERVER가 알아서..
B. DELETE 특히 취소 안댐
2. 명시적 트랜잭션
A. SET XECT_ABORT ON은 거의 여기선 필수인듯
3. 묵시적 트랜잭션
A. 자동 커밋 트랜잭션과 정반대 되는 개념이다.
B. 자동 커밋 트랜잭션 모드에서 DML 문을 수행하면 SQL SERVER가 자동으로 커밋해버려서 취소가 안된다.
C. 하지만! 묵시적 트랜잭션 모드에서는 DML문을 수행한 이후에 커밋과 롤백을 사용자가 결정한다!
트랜잭션과 락은 깊은 연관 관계를 가진다.
기본적으로 운영체제든 뭐던 간에 항상!! 항상!!! 동기화가 어려웠다. 스핀락이었나 그것도 그런 내용 같았는데 뭐 어쨋든..
ㄴ 맞다 히힣.. 기억이 좀 남아있긴한가보네
어쨋든 다시 돌아가서
이번에 잠금에 대해서 알아보게따.
요약)
공유잠금 S는 읽을때
단독잠금 X는 변경할때
공유잠금(S)이란 누가 읽고 있을 때 그 내용에 대해서 단독 잠금(X)를 못걸게 한다. 근데 공유잠금은 걸 수 있다.. 읽을 수 있다는 것이당
말로 좀 풀어서 상식적으로 생각해보면, 읽고 있는데 읽고 있는 내용을 누가 변경은 못한다는 것이다.
그 다음은 단독잠금 (X) 인데 단독 잠금은 데이터를 변경할때 거는 것이다.
1. 일단 단독잠금이 걸린 데이터는 다른 트랜잭션이 공유잠금을 걸 수 없다!!
2. 단독 잠금도 못건다!!! ( 오직 트랜잭션 하나만이 데이터에 대한 단독 잠금을 걸 수 있다 )
그라믄 좀 너무하지 않는가 바꾸고 있는거 읽을 수는 있게 해줄 수 있지 않는가
그래서 준비했다!
1. 잠금 힌트
2. 트랜잭션 격리수준으로 그걸 가능케!
그래서 어떻게 하는지 책에 안나와있어서 GPT로 좀 파보자.
1. 잠금힌트
A. 잠금힌트를 사용하면 SQL서버의 기본 잠금 동작을 오버라이드 하는 것이다.
B. 예를 들어서 NOLOCK 힌트를 사용하면 ( 이게 힌트라기 보다는 무슨 꼬리표 같은 느낌이네 ) 다른 트랜잭션이 단독 잠금을 가지 ㄴ데이터를 읽을 수 있다. 하지만 이 방법은 ‘더러운 읽기’(Dirty Read)를 발생시킬 수 있기 때문에 주의가 필요하다.
C. 어떻게 쓰는지 함 보자
i. SELECT * FROM Employees WITH (NOLOCK)
D. 아아 그 더러운 읽기 먼저 알아보자
E. 더티 읽기는 데이터 베이스에서 트랜잭션 처리과정 중에 발생할 수 있는 현상인데, 한 트랜잭션이 아직 커밋되지 않은 데이터를 다른 트랜잭션이 읽는 경우에 발생한다. 근데 더티까지는 아닌 것 같은데 더티라고 부르는 이유는 읽은 데C774ㅓ가 최종적으로 데이터 베이스에 반영되지 않을 수도 있기 때문이다.
F. 더러운 읽기의 예시를 알아보자
트랜잭션 A : 얘는 데이터를 수정하는중
BEGIN TRANSACTION
UPDATE Employees SET Salary = Salary + 5000 WHERE EmployeeID = 1;
( 아직 커밋 안함 )
트랜잭션 B : 얘는 동시에 실행중인 트랜잭션인데 읽으려나보다
SELECT Salary FROM Employees WHERE EmployeeID = 1;
이 경우에 트랜잭션 B는 아직 커밋을 안한 A의 데이터를 읽는 거기 때문에 (더러운) 걸로 치자.
아아아아아아ㅏㅇ아ㅏ아아 생각도 못한게 있어따.
생각해보니까 트랜잭션 A는 롤백될 수도 있다. 근데 롤백될 수 도 있는데 바꾸고 있는걸 읽게 되면 완전히 낙동강 오리알 될 수 있는거
( 이제 더럽다고 한 게 이해 되따 )
2. 트랜잭션 격리수준
3. 트랜잭션 중요도 수준
A. DEADLOCK_PRIORITY -10 ~ 10
B. DEADLOCK_PRIORITY LOW / NORMAL(default) / HIGH (5)
4. 근데 이것들이 왜 필요할까?
A. 데드락 상태 때문이다.
GO에 대해서 한번 그냥 궁금해져서
SQL은 배치batch를 하나의 단위로 해서 컴파일한다.
배치 내에서 선언된 로컬 변수는 해당 배치 안에서만 유효하다.
스코프 같은 느낌
각 배치는 서로 독립적으로 실행되어 한 배치의 실행이 다른 배치에 영향을 주지 않는다.
그래서 배치가 독립적으로 실행된다는게 병렬로 cpu가 실행하는건지가 궁금해져따
근데 그건 아니라고 하고
데이터베이스 안에서 별도의 실행단위로 처리된다는 것을 나타낸다고 한다.
하나의 배치가 완전히 실행되고 나면 다음 배치가 실행되며
각 배치는 서로 독립적인 컴파일과 실행 컨텍스트를 가진다.
SQL SERVER에서 배치 간의 독립적 실행이 의미하는 것은, 각 배치가 데이터베이스 엔진에 의해서 별도로 컴파일되고 실행된다는 것을 의미한다.
하나의 배치 안에서 실행된 명령은 다른 배치의 실행과는 별개로 관리된다.
이것은 배치 내에서 선언된 변수나 임시 테이블 등이 다른 배치와 공유되지 않음을 의미한다.
즉! 내가 생각했던 병렬 프로그래밍을 SQL SERVER에서는 사용하지 않는다!
다만, 쿼리 실행계획의 일부로 병렬처리를 사용할 수 있다고 한다.
데드락
세션
개요
SQL SERVER에서의 세션은 클라이언트 어플리케이션과 데이터베이스 서버 간의 연결을 의미함
세션은 사용자의 로그인부터 로그아웃까지, 또는 연결이 시작되어서 종료될 때까지의 지속되는 대화나 상호작용의 기간을 나타낸다.
각 세션은 고유한 세션 ID로 식별되며, SQL SERVER는 이를 통해서 동시에 여러 클라이언트의 요청을 처리할 수 있다.
세션의 특징 및 고유성
고유성
각 세션은 고유한 세션 ID ( @@SPID ) 를 가지며, 이를 통해 개별 클라이언트와의 상호작용을 관리한다.
상태유지성
세션은 클라이언트와 SQL SERVER 간의 상태를 유지한다. 예를 들어서, 세션 내에서 선언도니 변수나 트랜잭션 상태는 해당 세션에서만 유효
리소스관리
SQL SERVER는 각 세션에 할당된 리소스 ( 메모리, 잠금 등 )을 관리한다.
세션 종료시에 해당 세션에 할당된 모든 리소스가 자동으로 해제된다.
병렬처리
동시에 여러 세션을 처리함으로써, SQL SERVER는 다중 사용자 환경에서의 병렬 처리와 고성능을 지원한다.
SQL SERVER에서의 세션관리
세션 생성
클라이언트 어플리케이션이 SQL SERVER에 연결을 요청할 때 세션이 생성된다. 이 과정에서 로그인, 인증, 및 세션 초기화가 수행된다.
세션은 T-SQL 명령을 실행하고 결과를 클라이언트에 반환한다.
세션동안 여러 T-SQL 명령을 실행할 수 있으며, 각 명령은 독립적으로 또는 트랜잭션의 일부로 처리될 수 있다.
세션종료
클라이언트 어플리케이션이 연결을 종료하거나, 네트워크 오류 등으로 세션이 끊어지면, SQL Server는 세션을 종료하고, 해당 세션에 할당된 모든 리소스를 정리한다.
세션과 관련된 T-SQL 명령
@@ SPID : 시스템 전역변수 : 현재 세션의 세션 ID
KILL 세션 아이디 : 지정된 세션 ID의 세션을 강제로 종료함
트랜잭션 격리수준 ( Transaction Isolation Level )
SET TRANSACTION ISOLATION LEVEL ~~~
BEGIN TRANSACTION
END TRANSACTION
이 단어 잘 살펴보면 ACID 성질의
I가 들어가 있다. 그만큼 중요하다는거.
일단 SQL SERVER에서의 트랜잭션 격리수준이라는게 뭘 말하는 걸까.
트랜잭션의 실행 중 다른 트랜잭션으로부터 데이터를 어느정도로 보호할지 결정하는 설정이다.
격리수준은 데이터의 정확성과 동시성 사이의 균형을 조정하는데 사용된다.
다중 트랜잭션 중 발생할 수 있는 다양한 사고들
더티읽기
비반복읽기
팬텀읽기
SQL SERVER는 다섯가지 트랜잭션 격리수준을 지원한다.
SET TRANSACTION ISOLATION LEVEL
READ UNCOMMITTED
GO
SELECT * FROM dbo.Employee
WHERE EmpID = ‘S0001’
GO
트랜잭션 격리수준을 설정할건데, READ COMMITTED로 설정할거야.
가장 낮은 격리수준이고, 더티리드가 가능해. 보고 있는 와중에 롤백하게 되버리면 다른 결과를 보는거야
트랜잭션은 확실히 락에 들어오니까 어렵다.
한번 다시 정리해보자.
일단 락은 읽기 - 공유락S, 쓰기 - 단독락X이 있고, IX, IS, SIX를 적절히 활용해서 잠금된 데이터에 대한 식별 속도를 높인다.
알아두어야 할 것은
1. 트랜잭션 격리수준
SET TRANSACTION ISOLATION LEVEL ~~ 격리수준
A. READ UNCOMMITTED
i. Dirty Read 가능
ii. non-repeatable read 가능
iii. phontom read 가능
B. READ COMMITTED ( default )
i. Dirty Read 불가능 (당연히)
ii. non-Repeatable Read 불가능
iii. phantom read 가능
C. REPEATABLE READ
i. Phantom Read 가능
ii. non-Repeatable Read 불가능
D. SNAPSHOT
i. 트랜잭션이 시작될 때의 데이터 스냅샷을 기반으로 작업을 수행함
ii. 비반복 읽기와 팬텀읽기를 모두 방지하고, 동시에 동시성을 높인다.
E. SERIALIZABLE
i. 가장 높은 격리수준
ii. 트랜잭션이 읽거나 수정한 모든 데이터에 잠금을 걸어 다른 트랜잭션의 접근을 완전히 차단
iii. 동시성이 가장 낮지만, 데이터의 일관성과 정확성이 가장 높게 유지된다.
2. 테이블 힌트
쿼리문을 작성할때 FROM 절에 오는 테이블 이름 다음에 WITH 구문으로 옵션을 설정한다.
원래 쿼리 최적화기가 자동으로 하는 기능인데, 사용자가 강제로 원하는 형태로 하겠다는 것이다.
A. 잠금힌트
B. .... 이것저것 있는데 여기서는 잠금 힌트만 다룬다.
3. 좋지 않은 것들
A. Dirty Read
i. 커밋되지 않은 내용을 읽는 것
ii. 성능은 높으나, 단점은 당연히 위험하겠지. 불확실한 데이터인데
B. Phantom Read
i. 한 트랜잭션 내에서 쿼리를 두번 실행했을 때 첫번째 쿼리 실행과 두번째 쿼리 실행 사이에 다른 트랜잭션이 새로운 데이터를 삽입하거나 삭제하여 두 쿼리 결과가 다르게 나타내는 현상
ii. 예시
1. 트랜잭션 a가 특정 조건을 만족하는 모든 행을 조회한다.
2. 트랜잭션 b가 같은 조건을 만족하는 새로운 행을 삽입한 후 커밋한다.
3. 트랜잭션 a가 그 다음에 다시 같은 조건으로 쿼리를 실행했을 때, 새로 삽입된 행이 포함된 결과를 얻게 된다.
4. 결론적으로 같은 쿼리인데 다른 결과가 나오는거.
C. Non-Repeatable read
i. 일어나는 이유는 같은 데이터 행의 값 변경으로 인해서 발생한다.
4. 팬텀(유령)읽기와 비반복읽기가 헷깔린다.
A. 공통점은 두 현상모두 한 트랜잭션 내에서 데이터의 일관성을 유지하는데 문제를 일으킬 수 있으며, 다중 트랜잭션 환경에서 발생한다는 것
B. 차이점은
i. 비반복 읽기는 같은 데이터 행의 값 변경으로 인해서 발생하는 반면에
ii. 팬텀읽기는 쿼리 결과집합의 행 자체가 변경(추가나 삭제) 되어 발생한다.
iii. 정리하면 비반복 읽기는 데이터 값의 변경에 초점을 맞추고
iv. 팬텀읽기는 결과 집합의 구성 변경에 초점을 맞춘다.
여기서 말하는 현상은 사용자 A의 세션에서 실행하고 있는데 누가 낑겨서 값이 다르게 나오는거다. A는 당연히 유령이 있다고 생각할 수밖에 없다\
A입장에서 생각해보면 난 그냥 쿼리 잘 날리고 있었는데 갑자기 다른 값이 찍힌 거니까 이건 팬텀(유령)이다.
그러니까 팬텀현상이다. 팬텀현상은 READ COMMITTED로 못막아. 그러니까 그 아래 걸로도 못막음
1,3번
커밋되지 않은 내역을 읽어왔는데, 롤백해버렸다. 이건 READ NOT COMMITTED를 해서 일어난 문제이다.
READ NOT COMMITTED에서 가장 흔하게 발생하는 문제는 Dirty Read이다. 커밋이나 롤백하지 않은 데이터를 갖다가 그냥 읽어버려서 나는 문제이다.
정답은 1번
가장 쉽게 막을 수 있는게 Dirty Read인데, 여기서 말하는 가장 낮은 수준은 READ UNCOMMITTED이다.
READ UNCOMMITTED의 고립수준에서는 Dirty Read도 못막는다. 2,3,4번은 좀 더 상위차원의 문제이다.
따라서 1번이 정답이다. (1번을 모르더라도 풀 수 있는)
여기서 2명의 다른 세션에서 각각 트랜잭션을 날리고 있다.
시간순대로 정리하면
1번 2번
읽
쓰
읽
쓰
읽
롤백
따라서 이건 READ UNCOMMITTED에서 발생하는 문제로서 커밋되지 않은 (확정되지 않은) 데이터를 가져다 쓴 탓이다.
따라서 정답은 4번
자 이것도 한번 살펴보자.
다른 세션에서 각각 트랜잭션을 실행하고 있다.
트1 트2
READ COMMITTED설정
읽
REPEATABLE READ 설정
읽
근데 업데이트 하려고 했다
근데 이건 할수가 없어
REPEATABLE READ 설정을 하면 설정한 이후로 공유락 말고는 걸수가 없어
데이터의 변경이 불가능하다. ( 해당하지 않는 데이터에 추가는 가능했던 것 같음 ) -> 맞음 DELETE UPDATE만 불가능
따라서 가에서는 10000이 적용되고
나에서는 기다려야 한다.
나에서는 10100이 적용된다.
따라서 정답은 2번
일단 1,3,4번에 대해서 모른다. 그래도 풀 수 있을 것 같다. 트랜잭션 내용을 보자
먼저 다른 세션에서 각각 같은 데이터에 대해서 트랜잭션을 시간차를 두고 실행했다.
읽
읽
쓰
쓰
이다. 근데 T1이 T2로 인해서 바로게 저장되지 않았다고 하니까..음 .. 애초에 T2에서의 X는 T1에서의 변수값에 관여 할 수 없다 ( 세션이 다르면, 변수값에 개입 못함. 정확히는 배치단위로 변수는 관리되기 때문에 변수값에 개입을 하지 못한다. 때문에 같은 세션인가?
정답은 1번일 것 같다. 왜냐하면 T1의 세션에서의 X값이 오염되었을 수 있고 이건 갱신 손실이라는 말이 더 잘어울리는 것 같다. 4번도 그럴듯하긴한데..
정답!
음 근데 모범 답안은 살짝 다름. 생각해보니까 여기서 중요한건 저 T2의 쓰기였다.
아 이래서 이게 어려운건가.
페시미스틱 락 – 비관적 락
1. 트랜잭션이 시작될 때 공유락 S와 베타락 X를 걸고 시작하는 방법이다.
2. 어떤 데이터를 변경해야하는데 이 데이터에 대해서 다른 세션의 사용자의 트랜잭션의 공유락 S가 걸려있는 상황이라면, 업데이트를 할 수 없다.
3. 때문에 수정을 하기 위해서는 해당 트랜잭션을 제외한 모든 트랜잭션이 종료 ( COMMIT ) 되어야 한다.
상황 설명
T2가 DB의 ID가 2인 데이터 행에 대해서 베타락을 걸고 싶어한다.
근데 T1이 거따가 공유락을 이미 걸어버려놓은 상태이다.
그래서 걔가 트랜잭션을 완료 (종료) 할때까지 기다린다.
그럼 이번에는 좀 더 자세히 살펴보면
1. T1에서 ID 2번을 읽음 – S
2. T2에서 ID 2번을 읽음 – S
3. T2에서 그 데이터 변경하고 싶어함 – X
4. T1에서 트랜잭션을 종료 – COMMIT
5. 그리고 블록킹 되어있던 T2의 UPDATE 요청 처리
6. 이렇게 트랜잭션을 이용하여 충돌을 예방하는 것이 바로 비관적 락이다.
다음으로는 옵티미스틱 락 ( 낙관적 락 )을 보자
일단 상황분석부터! 그림이 위에가 없네
각각 다른 세션에서 트랜잭션을 실행하고 있다.
먼저 A가 2번 데이터에 대해서 공유락 S 를 건다. 그 다음에 B가 그 데이터에 대해서 공유락을 건다
그 다음에 B가 그 데이터에 대해서 베타락 X을 건다 그 다음에 A가 베타락을 걸었으나 오류날 것 같다. 왜냐하면 지금 팬텀읽기가 발생해따
A가 원하는 조건의 행은 이미 B에 의해서 바뀌어버림. A입장에서는 B가 유령(팬텀) 인거
1. 아 낙관적 락은 이미 이게 낙관적 락인거구나 ㅋㅋ........
2. A의 마지막 쿼리는 씹힌다.. VERSION은 충돌방지로 있던 거였으
A. 이게 낙관적 쿼리는 맞긴한데, 버전이 아니라 주로 HASHCODE나 타임스탬프로 한다고 한다.
B. 그래서 구분 칼럼을 쓰는게 낙관적 컬럼이라고 한다
i. ( 생각 없이(?!) 낙관적인 것 같다..)
ii. 집에 불났으면 집에서 안자면 되지 하는 느낌
빠르다.. 파이썬만큼 개발이 빨라질 수 있다는데 파이썬은 싱글스레드인데 C#은 멀티스레드..?
비동기, 바인딩 쉽다..?
Razor Pages : 간단한 프론트
Blazor : 프론트
ASP.NET
1. SSR
ASP.NET core
1. 주로 백엔드로 쓰고
2. RAZOR를 프론트에 쓸 수 있다.
스캐폴딩
1. 뷰, 컨트롤러를 자동으로 만들어준다는데 CRUD는 그냥 금방이라는데 ( 말이 되느..?)
EF CORE (entity framework core)
1. 알아서 db짜줌
2. LINQ가 뭔데 엄청 극찬이 많네
LINQ(?)
C# 정리
C#에서의 필드
필드는 클래스 또는 구조체 내부에 선언된 변수를 의미한다. 이들은 객체의 상태를 저장하는데에 사용되며, 인스턴스 필드와 정적 필드로 나뉜다.
Random의 인스턴스를 생성할때 static, readonly로 하는이유
1. 스레드 안전 : 랜덤 클래스 자체는 여러 스레드에서 동시에 사용할 경우에 스레드 안전하지 않을 수 있다.
2. 하지만 static으로 한번만 인스턴스 화하여 전체 어플리케이션에서 공유하면 안전성을 관리하기 쉽다.
업캐스팅
공변성
공변성은 코드의 재사용성과 유연성을 높여준다. 특히, 다양한 타입의 객체를 다루는 컬렌션, 대리자, 인터페이스 등에서 타입 안전성을 유지하면서도 더 넓은 범위의 타입 호환성을 제공한다.
C#에서 공변성을 활용하면, 보다 일반적인 타입으로 작업하면서도 타입의 구체적인 세부사항에 유연하게 대응할 수 있다.
공변성 – out 키워드
out 키워드는 인터페이스의 제네릭 타입 매개변수 T가 반환타입으로만 사용된다.
인터페이스 내의 메서드에서 해당 타입의 객체를 생성하거나, 메서드 매개변수로 사용하지 않을 경우에 적용된다.
그래서 이러한 제약으로 인해서, 공변성이 있는 인터페이스는 해당 제네릭 타입에 대해서 읽기 전용 동작만 수행할 수 있다.
IEnumarable 에 대해서 자세히 알아야 한다.
시퀀스
시퀀스는 한글로 하면 ‘순서’이다. 일련의 요소들이 순서대로 나열된 구조를 의미한다.
c#와 같은 프로그래밍 언어에서 시퀀스는 데이터를 순차적으로 저장하고 접근하는데에 사용되고, 각 요소는 특정 순서를 가지고 있다.
시퀀스의 개념은 배열, 리스트, 링크리스트, 큐, 스택 등 다양한 데이터 구조에서 찾아볼 수 있음
특징
순서성
반복가능성 ( Iterability ) - Iterable이라는 단어도 잘 알아야 함. Enumarable 순회가능한 Iterable 반복 가능한
길이 또는 크기
대표적인 시퀀스
대표적인 시퀀스는 LINQ와 IEnumerable이다.
IEnumerable : 모든 시퀀스 형태의 컬렉션 ( EX. 리스트, 배열등 )은 IEnumerable또는 IEnumerable 인터페이스를 구현한다.
이 인터페이스를 통해서 컬렉션을 순회하고 각 요소에 접근할 수 있는겨
LINQ 쿼리 Language Integrated Query를 사용하면 다양한 데이터 소스에서 시퀀스를 쿼리할 수 있다.
다양한 데이터 소스는 뭐냐!
배열 / 리스트 / XML문서 / 데이터베이스 테이블 등
LINQ 쿼리는 IEnumerable를 반환하며, 이를 통해 결과 시퀀스를 순회하고 처리할 수 있다.
클래스가 정적 클래스면 멤버가 모두 정적 멤버여야 하나..
정적 멤버가 있다고 해서 클래스가 정적 클래스여야 하는 것은 아니다.
딕셔너리
암시적 타입 배열
지연평가
지연평가에 대해서 알아보긴했지만, 한번 더 보는 김에 살펴보자면, LINQ 쿼리의 실행을 쿼리가 실제로 필요할 때까지 지연시키는 프로세스이다.
쿼리 변수가 선언 되었을 때 쿼리가 실행되는 것이 아니라, 쿼리결과에 접근하는 순간 실행되는 것이다.
예를 들어서 var result =
from ~~~
select ~~ 했다고 치자.
근데 여기서 쿼리가 실행되는 것이 아니라 저 result를 가지고
foreach(var item in result) 를 했을 때 (열거) 하였을 때 실행된다는 것이다.
LINQ Key
예를 들어서 Card 객체가 이런식으로 생겼다고 쳐보자
{
Suit = Hearts, Value = Jack
}
저게 여러개 있는 List deck이 있다고 쳤을때 말이지...
다음 LINQ를 실행한다고 쳐보자.
from card in deck
group card by card.Suit
여기를 보면 Suit가 그루핑의 기준이 되고 있다. 그럼 card.Suit이 KEY가 된다.
into SuitGroup
order by SuitGroup.Key descending
select suitGroup;
다음은 into를 살펴볼까.
일단 GPT에게 물어본 LINQ의 into 구문을 살펴보자.
이 구문은 그룹화 연산을 수행한 이후에 그룹화된 결과에 대해서 추가적인 쿼리 연산을 수행하고자 할 때 유용하다.
into키워드는 기본적으로 LINQ쿼리의 결과를 새로운 변수에 저장하고, 이 변수를 사용하여 더 많은 쿼리 연산을 연속적으로 체이닝할 수 있게 해준다.
into의 역할과 사용예를 보면
1. 그룹화 후에 연속 작업을 할때
2. 쿼리의 가독성을 향상 시킬 때
3. 쿼리 결과를 재사용 할 때
메서드체이닝을 사용하려면 객체를 하나 새로 생성해서 그 참조를 반환해야 한다.
IGrouping 인터페이스
IGrouping<TKey, TElement> 인터페이스는 LINQ 기능의 일부로, 키에 의해서 그룹화된 객체의 컬렉션을 나타낸다.
IGrouping<TKey, TElement>는 IEnumerable를 상속받으며, 특정키 TKey와 관련된 요소 TElement의 시퀀스를 나타낸다.
예시 코드를 보자
var groupedBySuit = deck.GroupBy(card => card.Suit);
foreach( var group in groupedBySuit ){
Console.WriteLine(”Suit : {group.Key}”); foreach( var card in group ){ Console.WriteLine(”Card : {card.Value}”);
}
}
IReadOnlyDictionary
readonly 타입
readonly 구조체를 보자.
C# 7.2 이상에서는 구조체 전체를 ‘readonly’로 선언할 수 있다. 이는 구조체의 모든 필드가 읽기 전용임을 의미한다.
구조체의 인스턴스가 생성된 후에는 그 상태가 변경될 수 없음을 보장한다.
코드를 보자
public readonly struct ReadonlyStruct{
public int X {get;}
public int Y {get;}
//생성자
public ReadonlyStruct( int x, int y ){
X = x;
Y = y;
}
}
LINQ 조인
IEnumerator 인터페이스
IEnumerator 인터페이스는 컬렉션의 현재 위치를 추적하고, 컬렉션을 순회하면서 각 요소에 접근할 수 있는 방법을 제공한다.
주로 다음의 메서드들을 포함한다.
1. Current
A. 컬렉션의 현재 요소를 가져온다.
2. MoveNext()
A. 열거자를 컬렉션의 다음요소로 이동한다.
3. Reset()
A. 열거자를 초기위치로 리셋한다.
C#클래스에서의 속성
알아야 할 것들 간단하게 정리
봉인된 클래스 Sealed
A. 더이상 재정의 당하고 싶지않아.
i. 맨 위나 맨 아래는 쓸일이 없음
대리자 Delegate
A. 제일 좀 솔직히 헷깔린다. 왜 필요한지도 잘 모르겠어.
B. 보면 병렬 프로그래밍이 있고
i. 거기에 멀티 스레딩이 포함이 된다.
ii. 근데 delegate를 들었을때 멀티스레딩인가 하는 느낌이 들었었는데 chatgpt에 물어보니
iii. delegate를 사용하여 다른 스레드에서 메소드를 호출하게 하는 것은 멀티 스레딩의 일종이고 이를 통해서 병렬 프로그래밍을 구현할 수 있다고는 했는데..
iv. detegate 자체가 멀티 스레딩을 직접 구현하지는 않는다.
v. 대신에 delegate를 사용하여 멀티 스레딩을 위한 메소드를 쉽게 전달하고 호출할 수 있다고 한다.
delegate와 multi threading의 차이
A. 먼저 목적이 다르다고 한다.
i. 대리자의 목적은 메소드를 변수처럼 전달하고 호출하는 것이고
ii. 멀티스레딩은 동시에 여러 작업을 수행할 수 있게 하는 것이다.\
ASP.NET CORE rest api
.NET6에서는 컴파일러가 using 문을 알아서 처리해서 , using문이 없다... 갓.
어플리케이션 인스턴스 (app) 생성
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
builder에는 Services 속성이 있다. Services 속성을 사용하여
Service 속성을 사용해서 CORS, Entity Framework, Swagger와 같은 기능을 추가할 수 있다.
예시로 CORS 설정 추가하는거
builder.Services.AddCors(options => {});
builder 생성
builder 설정
app 인스턴스 생성
과 같은 순서임
dotnet run\
람다식은 자바스크립트 익명함수랑 되게 비슷하다.
기본적으로
(parameter1) => {
return result;
}
혹은 진짜 축약하면
parameter1 => result 까지 가능한데
람다식은 자바스크립트 es6의 익명함수의 최고축약형에서 시작한다고 보면된다.
C#이 객체지향의 장점과, 함수형 프로그래밍의 장점을 모두 가져가려고 하는 것 같다는 생각이 들었다.
어쨋든 C#에서의 람다식 사용방법은 다음과 같음. 뭐 문람다, 식람다 이러는데 분류할 필요 없음
protected override void DoJob() {
HoneyVault.CollectNectar(~);); }; 인거다.
}는 기본적으로 안에 문이 들어가 있다. 무언가를 반환하는 식은 아니다.
이거 자바스크립트 익명함수로 바꾸면
const doJob = () => { HoneyVault.CollectNectar() }
이거인데 람다식도 마찬가지로
protected override void DoJob() => { HoneyVault.CollectNectar(
dojob이 참조인건 어차피 똑같음
그럼 만약에 dojob이 어떠한 값을 반환하는 식이라고 쳐보자.
그럼 protected override void DoJob() => 값 ;
하면 끝인거. 맨 아래꺼를 식람다라고 한다는데 좀 주의해야하는건 이 식람다를 사용해서
변수에다가 조건부나, 할당하려고 한다고 쳐보자
public string Name => “Suwon”; 이런식인겨.. 근데 이게 변수자너. 정확히 말하면 속성이자너.. 근데 이걸 람다로 표현하면 메서드처럼 작동한다는겨 물론 상관은 없겠지
public string Name(){
return “Suwon”;
} 이런식으로 작동하는데, 예를 들어서 이게 그냥 인스턴스 변수였을 때에 처음에 인스턴스 1번만 생성하면 그거 계속 쓸수 있는건데
메서드니까 아마도 계속 메모리에 올라가야할겨. 그러면 작동 똑같이 해도 소모적인겅께, 이걸 갖다가 리팩토링이라고 하면 안된다고 한다.
자바스크립트가 편한건가 아님 이게 맞는건가 하여튼. 이렇기 때문에 C#이 더 멋있는 것 같기도 하고, 자바스크립트는 좋은 언어는 아님. 아무튼 그럼 ㅇㅇ . ..
아 자바스크립트랑 C#이 작동하는게 달라서 코드가 잘 안읽힌다. ㅠ
DI 의존성주입를 알아보자. 자바스크립트에서는 없다.
Dependency Injection이라는 뜻으로 OOP에서 의존성( 즉 객체의 인스턴스 )를 외부에서 주입하는 디자인 패턴이다.
ASP.NET CORE와 같은 프레임워크에서는 이 패턴을 사용해서 어플리케이션의 결합도를 낮추고, 유닛테스트와 유지 보수성을 향상시킨다고 한다.
DI컨테이너(서비스 컨테이너)는 어플리케이션 실행중에 필요한 객체 인스턴스를 제공한다.
예를 들어서 하단의 코드에서는
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "Todo API", Description = "Keep track of your task"});
});
C가 처음에 AddSwaggerGen메서드가 실행될때 호출되면서 SwaggerGenOptions 객체를 매개변수 c로 받는다. 그렇게 람다표현식을 사용해서 DI를 통해 제공된 객체에 접근하고 그 객체를 사용하여 필요한 작업을 안에서 하는거다.
최소 api 확장하기
GET
app.MapGet(“/products”, () => data);
app.MapGet(“/products/{id}”, (int id) => data.SingleOrDefault(product => product.ID == id););
POST
app.MapPost(“/products”, (Product product) => /**/ );
record 데이터타입
불변성을 가진 객체를 생성하기 위한 특별한 종류의 클래스
주로 값 기반의 객체 비교, 간결한 구문을 통한 데이터 모델링, 불변 객체의 생성을 위해서 사용된다.
데이터를 담는 용도로 쓰이는 객체에서, 객체의 식별이 객체가 담고 있는 데이터로 이루어지길 원할 때 유용하다.
record는 불변성과 값에 의한 동등성을 가진 객체를 쉽게 생성하고자 할 때 매우 유용하다.
var person1 = new Person("John", "Doe");
var person2 = new Person("John", "Doe");
public record Person(string FirstName, string LastName);
이것은 한줄짜리 Person record이나 아래의 클래스와 같은 역할을 수행한다.
public class Person
{
public string FirstName { get; }
public string LastName { get; }
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
protected bool Equals(Person other)
{
return FirstName == other.FirstName && LastName == other.LastName;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Person)obj);
}
public override int GetHashCode()
{
return HashCode.Combine(FirstName, LastName);
}
}
그래서 마저 Post를 살펴보자.
app.MapPost(“/products”, (Project project) => /**/ );
Product는 이렇게 생김
{
"Name" : "New product",
"Description": "a description"
}
이것과 똑같은 record 형 클래스를 만들어보려면
public record Product(int Id, string Name);
PUT
체계가 MapPost와 비슷
클라이언트가 변경 내용을 포함하는 리소스와 함께 게시된 본문을 전송하는 것
변경 내용은 기존 리소스에 적용
app.MapPut(“/products”, (Product product) => / product 인스턴스를 사용하여 데이터 저장소를 업데이트 한다 / );
Delete
MapDelete
app.MapDelete(“/products/{id}”, (int id ) => / 유니크한 값인 id와 일치하는 레코드를 제거한다 /);
응답반환
기본적으로, 특정한 형식을 사용해서 응답하는 경우에 프레임워크는 이러한 형식을 JSON으로 직렬화 해야한다는 것을 인식하고 있다.
코드 작성 다시 해보자
아 먼저 데이터 직렬화를 한번 다시 보고 가자.
데이터 직렬화는 메모리를 디스크에 저장하거나 네트워크 통신에 사용하기 위한 형식으로 변환하는 것을 말한다.
역질렬화는 반대로 디스크에 저장한 데이터를 읽거나, 네트워크 통신으로 받은 데이터를 메모리에 쓸 수 있도록 다시 변환하는 것이다.
그럼 왜 직렬화를 해야할까?
데이터는 2가지 종류가 있다. 값 형식 데이터가 있고 참고형식 데이터가 있다. 이 두가지 중에서 디스크에 저장하거나, 통신에는 값형식 데이터만 할 수 있고, 참조형식 데이터는
실제 데이터 값이 아니라 힙에 할당되어있는 메모리 주소를 가지고 있기 때문에 저장 , 통신에 사용될 수 없다.
애초에 힙이 동적 영역이기 때문에 계속 바뀌어서 의미도 없고, 그걸 통신에 썼다가는 의미없어질 수 있다.
1. /products GET요청 응답 : data
app.MapGet(“/products”, () => data);
2. /products/{id} GET 요청 응답 : ID에 해당하는 데이터 반환
app.MapGet(“/products/{id}”, (int id) => products.SingleOrDefault(product.id == id) );
3. 기존걸 가져오는거말고 새로운 객체 생성해서주기
app.MapGet(“/products”, () => new {id = 1});
ASP.NET CORE에는 API가 두종류가 있다.
하나는 최소 API
굉장히 간결함 ( 근데 DB 연결은 안해봄 )
람다 또는 메서드에서 논리 처리기를 사용해서 엔드포인트를 정의한다.
기본적으로 호스트 클래스를 숨기고, 함수를 람다 식으로 사용하는 확장 메서드를 통해 구성 및 확장성에 중점을 둔다.
하나는 컨트롤러 기반 API ( MVC 패턴 )
컨트롤러는 생성자 주입 또는 속성 주입을 통해서 종속성을 사용할 수 있는 클래스이며, 일반적으로 객체 지향 패턴을 따른다.
컨트롤러를 기반으로 하는 API에 대한 샘플 코드를 작성해보자
namespace APIWithControllers;
public class Program{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.MapControllers();
app.Run();
}
}
using Microsoft.AspNetCore.Mvc; // mvc!
namespace APIWithControllers.Controllers;
[ApiController]Route(“[controller]”)]
public class WeatherForecaseController : ControllerBase
{
private static readonly string[] Summaries = new []{
“Freezing”, “Bracing”, “Chilly”,,,,,,,,,,,,
};
private readonly ILogger _logger; //로그남기려고
public WeatherForecaseController( ILogger logger){ // Constructor
_logger = logger;
}
}
대망의!
[HttpGet(Name = “GetWeatherForecast”)]
public IEnumerable Get(){ //순회 가능한 인터페이스 객체를 받아서
return Enumerable.Range(1,5).Select( index => new WeatherForecast{ // 이게 이런말인가 1~4까지의 시퀀스를 받아서
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summeries.Lengh)]
}).ToArray();
}
아래 코드가 더 가독성이 좋은 것 같아.
public IEnumerable Get()
{
var forecasts = new List(); // WeatherForecast 객체를 저장할 리스트 생성
foreach (var index in Enumerable.Range(1, 5))
{
var forecast = new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
};
forecasts.Add(forecast); // 생성된 forecast 객체를 리스트에 추가
}
return forecasts; // 생성된 리스트 반환
}
컨트롤러 API가 최소API에 비해서 더 가지고 있는 기능
1. 모델 바인딩
2. 유효성 검사
3. 기본제공 뷰 렌더링 지원하지 않음
4. JsonPatch를 지원하지 않음
5. Odata를 지원하지 않음
4,5번은 뭐 말하는건지 잘 모르겠음
리플렉션
1. 리플렉션은 프로그램이 실행 중에 자신의 구조를 검사하고, 수정할 수 있는 기능을 제공한다.
2. 그래서 다양한 동작들이 가능해지는데
A. 런타임에 객체의 타입을 검사하거나
B. 타입의 멤버에 (메서드, 속성, 필드 등)에 접근
C. 인스턴스를 생성
D. 메서드 호출 등이 모두 가능해진다.
3. 그러나 오버헤드가 가능하기 때문에 조심해서 써야한다.
4. 사용예제
A. 타입정보 얻기 : Type 클래스를 사용해 객체의 타입 정보를 얻을 수 있다.
Type typeInfo = typeof(MyClass);
B. 멤버 정보 접근 : GetMethods(), GetProperties(), GetFields() 등의 메서드를 사용해 해당 타입의 메서드,
MethodInfo[] methods = typeInfo.GetMethod();
C. 인스턴스 생성 : Activator.CreateInstance() 메서드를 사용해 런타임에 객체의 인스턴스를 생성할 수 있다.
object obj = Activator.CreateInstance(typeInfo);
D. 메서드 호출 : MethodInfo.Invoke()를 사용해 특정 메서드를 호출할 수 있다.
MethodInfo methodInfo = typeInfo.GetMethod(“MethodName”);
methodInfo.Invoke(obj, new object[] { 매개변수 값 });
ActionResult 형식
1. 개발자가 컨트롤러의 액션메소드에서 단일 타입으로 다양한 HTTP 응답을 반환할 수 있게 해준다.
2. 일반적인 ActionResult 타입을 확장한 것이다.
3. ActionResult를 사용하면
A. 액션 메소드에서 성공 응답으로 모델 데이터를 반환하거나
B. 다양한 종류의 에러응답 (NotFound, BadRequest 등)을 반환할 수 있다.
4. 그럼 Task<ActionResult<IEnumerable>>을 한번 보자. 이게 뭐하는 건지.
A. .NET 프레임워크의 System.Threading.Tasks에는 병행성 코드나 비동기 코드의 실행을 돕는 클래스들이 들어있다.
B. Task 또한 내부적으로 Thread로 구현된다.
C. Task 클래스를 사용하여 비동기 코드를 작성할 수 있다.
D. 한번 뜯어보면
i. Task<> : 비동기작업, 완료되면 그 결과를 포함하거나 완료시점을 알린다.
ii. ActionResult<> : asp.net core에서 액션 메소드의 반환 유형으로 사용되며, http 응답을 나타낸다.
iii. IEnumerable : TodoItem 객체의 컬렉션을 나타내며, 여기서는 여러 TodoItem 개체를 순회할 수 있는 열거 가능한 시퀀스이다.
iv. 즉 위의 타입을 해석하면,
1. 비동기 작업으로, 완료될 때 IEnumerable 컬렉션을 포함하는 ActionResult를 반환할 것임을 나타낸다.
2. 이것은 asp.net core에서 데이터를 비동기적으로 조회하고 결과를 http응답으로 반환할때 매우 흔히 사용되는 패턴이다.
ToListAsync()
1. Entity Framework Core에서 비동기적으로 데이터를 조회하고 결과를 리스트로 변환할때 사용.
2. LINQ 쿼리의 결과를 List형태로 비동기적으로 변환하기 위해 사용되며, System.Threading.Tasks 네임 스페이스 안에 있는 Task를 반환한다.
3. 이를 통해 데이터베이스 작업을 비동기적으로 수행함으로써 어플리케이션 반응성을 향상 시키고, 리소스를 보다 효율적으로 사용할 수 있게 한다.
EnsureSuccessStatusCode
1. Http 응답이 실패한 경우 예외를 던진다.
Kestrel
1. asp.net core에 권장되는 서버
2. default
3. 새 asp.net core 프로젝트는 5000-5300 번 사이의 임의 http 포트와 7000 – 7300 사이의 임의 https 포트에 바인딩하도록 구성되고
4. launchSettings 파일에 저장되며 개발자가 수정할 수 있음
5. 엔드포인트 구성이 따로 없으면 5000번에 바인딩한다.
addSingleton
어트리뷰트
class {
[required]
// 해당 속성이 필수임을 의미, 데이터 모델이 유효성 검사를 거칠 때 이 속성의 값이 null이거나 빈 문자열이면 안된다.
[StringLenth(100)]
// 이 어트리뷰트는 문자열 속성의 최대 길이를 지정, 선택적으로 최소 길이도 지정할 수 있다.
public string Info{get;set;}
}
위의 빨간 표현은 어트리뷰트라고 불린다.
어트리뷰터는 메타데이터 형식으로, 클래서, 메서드, 속성 등에 추가 정보를 제공하는데 사용된다.
이 메타 데이터는 런타임 시에 리플렉션을 통해 접근할 수 있으며, 다양한 목적으로 사용될 수 있다.
ASP.NET core 같은 웹 프레임워크에서는 모델 바인딩 시에 이러한 어트리뷰트를 통해서 자동으로 유효성검사를 수행한다.
어트리뷰트의 작동방식
1. 어트리뷰트는 컴파일 타임에 소스코드에 추가되는 주석과 유사하다.
A. 그러나, 런타임 시에도 해당 정보에 접근할 수 있다.
2. 클래스나 멤버가 특정한 방식으로 동작해야 함을 선언할 수 있고, .NET프레임워크나 사용자 정의 코드에서 이 정보를 기반으로 추가적인 처리가 가능하다.
A. [required][StringLenth(100)] 같은 것들이 해당
3. 추가 어트리뷰트
A. 사실 되게 많다. 근데 일단 ASP.NET core 관련 어트리뷰트만 살펴보고 가자.
B. [ApiController]
i. 컨트롤러 클래스가 API 컨트롤러임을 나타내며, API 개발시에 유용한 여러 기본 동작을 활성화 한다.
C. [Route(“path”)]
i. 컨트롤러나 액션 메서드에 대한 라우팅 경로를 지정한다. URL 패턴과 매칭되는 경로를 설정할 수 있다.
D. 나머지 몇개 더 있는데 그건 나중에 보고
4. 왜 어트리뷰트를 쓰나?
A. 코드의 의도를 명확하게 표현하고, 프레임워크 레벨에서 자동으로 처리할 수 있는 건 자동으로 처리하자
i. 개발자의 작업을 줄일 수 있고, 코드의 안정성과 유지 보수성을 향상시킬 수 있다.
그 다음은 리플렉션을 살펴보자. 위의 어트리뷰트의 동작과 리플렉션은 관련이 있다.
FieldInfo 데이터타입 참고하고..
1. 리플렉션 왜 쓰냐
A. dynamic을 알아야함
B. 조금이라도 동적으로 개발하려고
i. 런타임에 특정형식의 인스턴스를 만들 수 있으니까
1. 음.. 아직은 모르겠는데 더 살펴보자
2. 뭐가 좋은건지 잘 모르겠는데
ii. new 연산자와 비교해서 인스턴스를 Activator.CreateInstance() 로 만드는게 뭐가 좋은지를 잘 모르겠어서 gpt에게 물어봄
1. 근데 성능 오버헤드를 동반한다고 하니까 안쓰는걸 디폴트로 놓고가자
iii. 써서 좋은점 (new에 비해서)
1. 실행시간에 결정되는 타입으로 객체를 생성해야할 때 리플렉션을 사용한다.
A. 즉 컴파일 시간에는 해당 타입이 무엇인지 알수 없지만 런타임에 타입이 결정되는 경우
asp.net core에서의 종속성 주입 _ Dependency Injection
1. 클래스와 해당 종속성 간의 IOC를 실현하는 기술 : DI 소프트웨어 디자인 패턴
2. asp.net core MVC 컨트롤러에 대한 종속성 주입
A. MVC컨트롤러는 생성자를 통해서 명시적으로 종속성을 요청한다.
B. asp.net core는 기본적으로 di를 지원한다.
i. di를 사용하면 앱의 테스트와 유지관리가 쉬워짐.. 왜?
= > 가 자꾸 헷깔린다.
1. 자바스크립트에서는 익명함수만 수행했지만, 여기서는 용도가 2가지라고 한다.
A. 람다식
i. 자바스크립트와 비슷
B. 표현식 본문멤버
IActionResult와 ActionResult를 비교해보자.
IActionResult의 주요 사용사례
1. 상태코드 반환
A. 특정 HTTP 상태 코드와 선택적으로 메시지를 반환할때 사용한다. 예를 들어서, NotFound(), BadRequests(), Ok()등의 메서드가 있다.
B. ASP.NET CORE에서는 주로 이렇게 쓰였다.
2. 데이터 반환
A. JsonResult 또는 OkObjectResult를 사용하여 JSON 형태의 데이터를 반환할 수 있다.
3. 파일 반환
A. FileResult 를 사용하여 파일 다운로드 응답을 생성할 수 있다.
테스트와 디버깅 in 마소 문서
1. Console로 체크하지마라..찔렸다. 일단 디버거 시작해라.
2. 디버거의 기능은 여러가지가 있지만 다음이 주요하다.
A. 프로그램 실행제어
B. 프로그램 상태관찰
3. 모든 예외는 Exception클래스를 상속함
4. 예외형식은 포함된 정보를 결정한다.
5. 어플리케이션 실행 결과로 시스템 오류가 발생하면 예외가 만들어지고 .NET 런타임에 의해서 throw 된다.
어제만들어본 dotnet web api를 다시 살펴보자.
1. 먼저 모델을 정의하고
2. 모델을 기반으로 서비스를 생성한다. 서비스는 주로 정적으로 이뤄진다.
3. 컨트롤러를 생성하여 서비스와 연결한다.
ASP.NET CORE MVC API 에서 SQLSERVER 연결하기
1. 진입점에서 설정 – program.cs
2. appsetting.json에서 연결 문자열 설정
A. 연결문자열은 "Server=D662-ETHANLIM;Database=SampleDBCodeFirst;Trusted_Connection=True;"
i. Server : 서버 이름
ii. Database : 연결할 특정 데이터베이스 이름
다음은 MVC API 에서의 launchSettings.json이다.
먼저 launchSettings.json은 asp.net core 어플리케이션을 개발할 때, 비쥬얼 스튜디오 또는 .net core cli를 사용하여 어플리케이션을 실행할 때 사용되는 설정을 정의한다.
이 파일은 개발 환경에서 어플리케이션을 실행하는 방법을 구성하는데 사용되며, 프로덕션 환경 설정에는 영향을 주지 않는다.
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
// iis express를 사용하여 어플리케이션이 실행될 때의 기본URL 지정
"applicationUrl": "http://localhost:59623",
"sslPort": 44376
}
},
"profiles": {
"http": {
"commandName": "Project",
// 프로젝트의 실행방법
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
// 브라우저에서 자동으로 열리는 URL을 지정, 지금은 swagger로 swagger ui 페이지를 자동으로 열도록 설정하였다.
"applicationUrl": "http://localhost:5259",
// 어플리케이션에 접근할 수 있는 URL을 지정한다. https 프로필의 경우네는 https, http 둘다 지원한다.
"environmentVariables": {
// 이게 중요. 어플리케이션 실행 시 설정할 환경변수를 지정한다.
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7152;http://localhost:5259",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
관행적으로 C#에서는 비동기로 작동할 메서드의 이름 뒤에 Async를 붙인다.
Task : 프라미스
C#에서 익명타입은 선언과 동시에 초기화되어야 하며, 형과 관련된 설정은 컴파일 시에 일어난다.
콜렉션
대리자 콜
비동기 함수에서는 다음 반환형만 허용된다.
이제 실제적인 DB 업로드나 프레임워크 사용법에 대해서 공부를 한다.
기본적으로 대부분의 API에 GET 요청을 보내면 대부분이 JSON 객체를 반환한다.
자바스크립트에서 JSON.stringify가 이러한 [JSON객체]를 JSON 형태의 문자열로 변환하는 역할을 했던 것이고, C#에는 이런 것이 있을지 보았다.
ORM – object Relational Mapping
.net 4.7.2 – entity framework 6.44와 호환
1. ADO.NET
// Database 연결
string connectionString = "Server=D662-ETHANLIM;Database=Test02;Trusted_Connection=True;TrustServerCertificate=True";
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
string sql = "SELECT * FROM cart";
var command = new SqlCommand(sql, connection);
using(SqlDataReader reader = command.ExecuteReader()) {
while(reader.Read())
{
Console.WriteLine(reader["UserId"].ToString());
}
}
}
"dbControllerContext": "Server=D662-ETHANLIM;Database=test02;Trusted_Connection=True;TrustServerCertificate=True"
"dbControllerContext": "Server=D662-ETHANLIM;Database=test02;Trusted_Connection=True;TrustServerCertificate=True"
"dbControllerContext": "Server=D662-ETHANLIM;Database=test02;Trusted_Connection=True;TrustServerCertificate=True"
"dbControllerContext": "Server=D662-ETHANLIM;Database=test02;Trusted_Connection=True;TrustServerCertificate=True"
"dbControllerContext": "Server=D662-ETHANLIM;Database=test02;Trusted_Connection=True;TrustServerCertificate=True"
"dbControllerContext": "Server=D662-ETHANLIM;Database=test02;Trusted_Connection=True;TrustServerCertificate=True"
"dbControllerContext": "Server=D662-ETHANLIM;Database=test02;Trusted_Connection=True;TrustServerCertificate=True"
"dbControllerContext": "Server=D662-ETHANLIM;Database=test02;Trusted_Connection=True;TrustServerCertificate=True"
제발!!!!! 끝에 인증서 저거 빼먹지말자
데이터베이스 – 코드 안의 객체로 매핑할때 디비를 먼저 만들어버리면 스키마대로 모델을 수정해야하는 번거로움이 생긴다.
그래서 보면 EF(6) CodeFirst Migration을 사용하면, 디비를 만들어주고, 스키마 관리도 자기가 한다.
여러모로 EF를 잘봐야
https://learn.microsoft.com/ko-kr/ef/core/managing-schemas/migrations/managing?tabs=dotnet-core-cli
c#에서 앞에 @이 붙어있는 것은 해당 문자열이 ‘정확한 문자열 리터럴’임을 나타낸다. 특히 파일 경로나 데이터베이스 연결 문자열과 같이 백슬래시를 포함하는 문자열을 표현할 때 유용하다. 기본적으로 C#에서 문자열을 사용하려면 이스케이프 문자열 포함 두개의 백슬래시 \가 연속으로 입력되어야 함. 근데 @을 쓰면 하나만 써서 경로나 연결 문자열을 나타낼 수 있음
Entity Framework Core 에서 Code first – Migration 사용할때
1. 먼저 필요한 패키지 깔고 ( 문서에 잘나와있음) 마이그레이션 개요 - EF Core | Microsoft Learn
2. 모델 설정하고
3. 명령어치면 스키마 자동으로 만들어줌
4. 컨텍스트 객체 가져와서 조작하면 된다.
디비 연결 문자열에 대해서 옵션을 아예 자세히 파보자.
FluentAPI
#region Required는 코드를 접었다 펼 수 있는 코드블록을 만든다.
ㅋㅋ 갑자기 궁금해졌다... asp.net 와 spring의 속도 차이가 궁금해졌다...
4. asp.net core가 대규모 처리시에 초당 HTTP요청이 제일 빠르다.
i. Servlet, nginx, go언어, node보다도..
5. db 엑세스 성능이나 초당 처리 성능이 상당히 빠름
6. net core로 넘어오면서 비동기나 각종 메모리 관리 등 성능이 크게 향상되었음
7. 게임 개발사가 상대적으로 자바보다 c#을 접할 확률이 더 큼
A. UNITY
8. net core를 통해서 다양한 환경에 배포가 가능해짐
A. 리눅스, 도커, ec2 등등..
9. 힙 메모리에 의존하는 연산을 최소화하고 있고
10. 가비지 컬렉터가 실행되는 빈도를 낮추고 있어서 인풋 아웃풋 연산을 최적화하는 것에 공을 많이 들였다..
A. 갓 마소
11. 아무리 Reactive 도구가 나오더라도 컴파일러 수준에서 지원하는 비동기 프로그래밍을 이길 수는 없다.
A. ASYNC AWAIT
12. 빠르기로 유명한 고랭보다도 빠르고,, 노드보다 10배 자바보다 2~5배, 루비보다 100배 ㅋㅋ..
A. 특히 병렬처리가 엄청 빠르다고 한다.
13.
우선순위
1. MSSQL ~ 2/2
A. 데이터조작)완
B. 내부구현)완
C. 인덱싱)완
i. 쿼리 튜닝은 천천히 좀 살펴봐야 할 것 같다.
ii. 실행 계획을 해석하는 법을 오랜 기간동안 공부해야해
D. 프로시저) 3/4 완
E. 트리거
i. DML
1. AFTER
2. INSTEAD OF
ii. DDL
1. 서버
2. 데이터베이스
F. 사용자정의함수) 완
i. 스칼라
1. 성능이슈
ii. 단일문
iii. 다중문
2. C#
A. C#
i. ~2/3?
B. ASP.NET
i. ~2/5
C. .NET
i. ~2/20
3. POWER BUILDER
4. IOS