SQL이 더 리팩터링되지 않는 이유는 무엇입니까? [닫은]


39

누구나 새로운 개발자가 긴 기능을 작성한다는 것을 알고 있습니다. 진행하면서 코드를 더 작은 조각으로 나누는 것이 더 좋아지고 경험에 따라 그렇게하는 것이 중요합니다.

SQL을 입력하십시오. 예, 코드에 대한 SQL 사고 방식은 코드에 대한 절차 적 사고 방식과 다르지만이 원칙은 적용 가능한 것처럼 보입니다.

다음과 같은 형식의 쿼리가 있다고 가정 해 보겠습니다.

select * from subQuery1 inner join subQuerry2 left join subquerry3 left join join subQuery4 

일부 ID 또는 날짜 등 사용

이러한 하위 쿼리는 자체적으로 복잡하며 자체 하위 쿼리를 포함 할 수 있습니다. 다른 프로그래밍 컨텍스트에서는 복잡한 하위 쿼리 1-4에 대한 논리가 모든 하위 쿼리를 결합하는 부모 쿼리와 일치한다고 생각합니다. 절차 적 코드를 작성하는 경우 함수처럼 하위 쿼리를 뷰로 정의하는 것이 매우 간단 해 보입니다.

그렇다면 왜 일반적인 관행이 아닌가? 사람들이 왜 이렇게 긴 모 놀리 식 SQL 쿼리를 작성 하는가? 절차 적 프로그래밍처럼 광범위한 뷰 사용을 장려하지 않는 이유는 무엇입니까? (많은 엔터프라이즈 환경에서 뷰를 작성하는 것은 쉬운 일이 아닙니다. 요청 및 승인이 필요합니다. 다른 유형의 프로그래머가 함수를 작성할 때마다 요청을 제출해야한다고 상상해보십시오!)

가능한 세 가지 답변을 생각했습니다.

  1. 이것은 이미 일반적이며 경험이없는 사람들과 협력하고 있습니다.

  2. 숙련 된 프로그래머는 절차 적 코드로 하드 ​​데이터 처리 문제를 해결하는 것을 선호하기 때문에 복잡한 SQL을 작성하지 않습니다.

  3. 다른 것


12
뷰를 통해 데이터베이스를 쿼리하고 저장 프로 시저를 통해 데이터베이스를 수정할 수있는 조직이 있습니다.
Pieter B

3
SQL이 일반적인 절차 코드만큼 건조하지 않을 것이라는 것을 마침내 받아 들였을 때 SQL이 훨씬 더 즐거워졌습니다.
Graham

1
4. SQL은 실제로 오래되었으며 수십 년 동안 실질적으로 업데이트되지 않았습니다. 매우 복잡한 작업의 경우 많은 팀이 저장 프로 시저를 선택합니다. 이에 대해 다른 절을 추가 할 수 있습니다. 때로는 임시 테이블에서 데이터를 스테이징하기 위해 작업을 실행 한 다음 조인해야합니다. 선언적 및 절차 적 언어가 얼마나 다른지보십시오.
베린 로리 치

8
또한 한 가지 이유는 뷰를 사용할 때 발생할 수있는 "삼각형 조인"이라는 끔찍한 성능 문제가 있기 때문입니다 (물론 우연히). 쿼리가 View A 및 View B에 참여하지만 구현에서 View A View B를 재사용하면 해당 문제가 발생하기 시작합니다. 따라서 사람들은 종종 단일 모 놀리 식 쿼리를 작성하여 뷰에 대한 리팩토링 측면에서 실제로 가장 잘 작동하는 것을 확인한 다음 마감 시간을 맞출 수 있으며 모놀리스는 프로덕션으로 이동합니다. 모든 소프트웨어 개발자의 98 % 정도, 실제로 :) :)
Stephen Byrne

3
"다른 유형의 프로그래머가 함수를 만들 때마다 요청을 제출해야한다고 상상해보십시오"... 음. 코드 검토를하지 않습니까?
Svidgen

답변:


25

주된 문제는 모든 데이터베이스가 공통 테이블 표현식을 지원하지는 않는다는 것입니다.

제 고용주는 DB / 2를 아주 많이 사용합니다. 최신 버전의 CTE를 지원하므로 다음과 같은 작업을 수행 할 수 있습니다.

with custs as (
    select acct# as accountNumber, cfname as firstName, clname as lastName,
    from wrdCsts
    where -- various criteria
)
, accounts as (
    select acct# as accountNumber, crBal as currentBalance
    from crzyAcctTbl
)
select firstName, lastName, currentBalance
from custs
inner join accounts on custs.accountNumber = accounts.accountNumber

결과적으로 우리는 테이블 / 필드 이름을 크게 축약 할 수 있으며 기본적으로 더 읽기 쉬운 이름으로 임시보기를 작성하여 사용할 수 있습니다. 물론 쿼리가 길어집니다. 그러나 결과는 꽤 명확하게 분리 된 것을 작성하고 (CTE를 사용하여 함수를 사용하여 DRY를 얻는 방법) 매우 읽기 쉬운 코드로 끝날 수 있다는 것입니다. 하위 쿼리를 분리하고 하나의 하위 쿼리가 다른 하위 쿼리를 참조 할 수 있기 때문에 모든 "인라인"이 아닙니다. 때때로, 하나의 CTE를 작성했고, 다른 4 개의 CTE가 모두 그것을 참조한 다음, 주 쿼리에 마지막 4 개의 결과를 통합했습니다.

이것은 다음과 같이 할 수 있습니다 :

  • DB / 2
  • PostGreSQL
  • 신탁
  • MS SQL 서버
  • MySQL (최신 버전; 여전히 새로운 기능)
  • 아마 다른 사람

그러나 코드를 더 깨끗하고 읽기 쉽고 건조하게 만드는 방향으로 길게 나아가고 있습니다.

다양한 쿼리에 플러그인 할 수있는 CTE의 "표준 라이브러리"를 개발하여 새 쿼리에서 빠르게 시작할 수 있습니다. 그들 중 일부는 내 조직의 다른 개발자들도 받아들이 기 시작했습니다.

시간이 지남에 따라 이러한 "표준 라이브러리"를 복사 / 붙여 넣기없이 사용할 수 있도록 이들 중 일부를보기로 전환하는 것이 좋습니다. 그러나 CTE는 여러 가지 요구 사항에 따라 조정되어 결국 CTE를 너무 많이 사용하여 개조없이 사용할 수 없었기 때문에 뷰를 만들 가치가 있습니다.

당신의 고민 중 일부는 "CTE에 대해 왜 잘 모르겠습니까?"인 것 같습니다. 또는 "DB가 CTE를 지원하지 않는 이유는 무엇입니까?"

업데이트에 관해서는 ... 그래, CTE를 사용할 수는 있지만, 내 경험상 set 절과 where 절에서 사용해야합니다. 전체 update 문보다 먼저 하나 이상을 정의한 다음 set / where 절에 "주요 쿼리"부분 만 있으면되지만 그렇게 작동하지 않는 것이 좋습니다. 그리고 업데이트하는 테이블에 모호한 테이블 / 필드 이름을 피할 수 있습니다.

CTE를 사용하여 삭제할 수 있습니다. 해당 테이블에서 삭제하려는 레코드의 PK / FK 값을 결정하려면 여러 CTE가 필요할 수 있습니다. 다시 한 번, 수정중인 테이블에서 테이블 / 필드 이름이 모호해지지 않도록 할 수 없습니다.

삽입물을 선택할 수 있으므로 삽입물에 CTE를 사용할 수 있습니다. 항상 그렇듯이 수정하려는 테이블에서 모호한 테이블 / 필드 이름을 처리 할 수 ​​있습니다.

SQL에서는 getters / setters로 테이블을 줄 바꿈하여 도메인 오브젝트와 동등한 것을 작성할 수 없습니다. 이를 위해서는보다 절차 적 / OO 프로그래밍 언어와 함께 어떤 종류의 ORM을 사용해야합니다. Java / Hibernate에서 이러한 성격의 것을 썼습니다.


4
Big CTE 씨는 최악의 SQL을 작성하는 사람이었습니다. 문제는 CTE가 추상화 선택이
Joshua

3
또한 ORM은 성능 측면에서도 매우 중요한 일을 할 수 있습니다. 특히 getter 및 setter를 사용하여 많은 데이터를 가져 오는 경우 특히 그렇습니다. 최대 절전 모드는 하나의 큰 조인 된 쿼리 대신 수백 개의 개별 쿼리를 사용하는 것으로 유명합니다. 이는 각 쿼리에 오버 헤드가있을 때 문제가됩니다.
user3067860

2
@Joshua 모든 언어로 잘못된 코드를 작성할 수 있습니다. SQL을 포함합니다. 그러나 CTE에 대한 리팩토링은 올바르게 수행하면 사람이 파싱하기 쉬운 상향식 설계를 만들 수 있습니다. 나는 내가 다루고있는 언어에 관계없이 바람직한 특성으로 보는 경향이있다. :-)
Meower68

2
다른 답변은 훌륭하지만 이것이 개인적으로 찾고있는 것입니다. 'CTE에 대해 왜 모르는가'가 내 문제의 대부분이었습니다.
ebrts

2
@ Meower68 CTE를 광범위하게 사용하면 사람들이 제대로 조인을 배우고 훌륭한 데이터베이스 디자인을 배우지 못하게 될 위험이 있습니까? CTE의 가치를 지원하지만 하위 쿼리를 사용하여 너무 쉽게 작업하지 않아도됩니다.
피터 B

36

데이터베이스 뷰 생성을 잠그는 것은 종종 데이터베이스의 성능 문제에 대한 편집증에 의해 수행됩니다. 이것은 SQL의 기술적 문제가 아닌 조직 문화 문제입니다.

그 외에도 유스 케이스가 너무 구체적이기 때문에 다른 쿼리에서 SQL 코드를 거의 재사용 할 수 없기 때문에 대규모 단일 SQL 쿼리가 여러 번 작성됩니다. 복잡한 쿼리가 필요한 경우 일반적으로 사용 사례가 훨씬 다릅니다. 다른 쿼리에서 SQL을 복사하는 것이 종종 시작점이지만 새 쿼리의 다른 하위 쿼리 및 JOIN으로 인해 다른 언어의 "함수"가 갖는 추상화를 깨뜨리기에 충분히 복사 된 SQL을 수정하게됩니다. 사용된다. 이것이 SQL을 리팩토링하기 어려운 가장 중요한 이유입니다.

SQL은 추상적 인 행동 (또는 단어의 의미에서 추상화)이 아닌 구체적인 데이터 구조 만 다룹니다. SQL은 구체적인 아이디어를 중심으로 작성되었으므로 재사용 가능한 모듈로 추상화 할 필요는 없습니다. 데이터베이스 뷰가이를 도와 줄 수 있지만 다른 언어의 "기능"과 같은 수준은 아닙니다. 데이터베이스 뷰는 쿼리만큼 추상화가 아닙니다. 실제로 데이터베이스 뷰 쿼리입니다. 본질적으로 테이블처럼 사용되지만 하위 쿼리처럼 실행되므로 추상이 아닌 구체적인 것을 다루고 있습니다.

추상화는 코드의 리팩토링이 더 쉬워집니다. 추상화는 해당 추상화의 소비자로부터 구현 세부 사항을 숨기기 때문입니다. Oracle 용 PL / SQL 또는 SQL Server 용 Transact-SQL과 같은 SQL에 대한 절차 적 확장은 선을 약간 흐리게 만들기 시작하지만 스트레이트 SQL은 그러한 분리를 제공하지 않습니다.


"SQL은 추상적 인 행동 (또는 어떤 의미에서든 추상화)이 아닌 구체적인 데이터 구조 만 다룹니다." 내 관점에서 SQL 은 단어의 의미에서 구체적인 프로그래밍이 아닌 추상적 동작을 전적으로 다루기 때문에 이상한 진술입니다 ! 단순한 단어 "JOIN"으로 추상화 된 모든 정도의 복잡성을 고려하십시오. 두 개의 서로 다른 데이터 세트에서 병합 된 결과를 원하고 DBMS에 맡겨 구체적인 기술을 결정하고 처리해야합니다. 인덱싱, 테이블 및 하위 쿼리 등의 차이점 처리 ...
Mason Wheeler

5
@ MasonWheeler : 언어 기능의 구현이 아니라 작동하는 데이터의 관점에서 SQL을 더 많이 생각하고 있다고 생각합니다. 데이터베이스의 테이블은 추상화처럼 보이지 않습니다. 전화 번호가 포함 된 "phone_numbers"라는 표와 같이 구체적입니다. 전화 번호는 추상적 인 개념이 아닙니다.
Greg Burghardt

12

귀하의 질문 / 관점에서 누락 될 수 있다고 생각하는 것은 SQL이 세트에 대한 작업 (세트 작업 등 사용)을 실행한다는 것입니다.

그 수준에서 작동하면 당연히 엔진에 대한 특정 제어권을 포기합니다. 커서를 사용하여 일부 절차 스타일 코드를 강제로 적용 할 수 있지만 경험에 따라 99/100 배가 표시되면 그렇게하지 않아야합니다.

SQL 리팩토링은 가능하지만 애플리케이션 레벨 코드에서 사용했던 것과 동일한 코드 리팩토링 원칙을 사용하지 않습니다. 대신 SQL 엔진 자체 사용 방법을 최적화합니다.

이것은 다양한 방법으로 수행 할 수 있습니다. Microsoft SQL Server를 사용하는 경우 SSMS를 사용하여 대략적인 실행 계획을 제공 할 수 있으며이를 사용하여 코드를 조정하기 위해 수행 할 수있는 단계를 확인할 수 있습니다.

@ greg-burghardt가 언급했듯이 코드를 더 작은 모듈로 분리하는 경우 SQL은 일반적으로 목적에 따라 작성된 코드 조각입니다. 그것은 당신이해야 할 한 가지 일을하고 다른 것은 없습니다. SOLID의 S를 준수하고 있으며 변경 / 영향을받는 한 가지 이유 만 있으므로 해당 쿼리가 다른 작업을 수행해야하는 경우입니다. 나머지 약어 (OLID)는 여기에 적용되지 않습니다 (AFAIK는 SQL에 의존성 주입, 인터페이스 또는 종속성이 없음) 사용하는 SQL의 풍미에 따라 특정 쿼리를 줄 바꿈하여 확장 할 수 있습니다 저장 프로 시저 / 테이블 함수에서 또는 하위 쿼리로 사용하므로 열린 폐쇄 원칙이 여전히 적용됩니다. 그러나 나는 산만하다.

SQL 코드를 보는 방식에서 패러다임을 바꿔야한다고 생각합니다. 그 특성으로 인해 응용 프로그램 수준 언어 (일반) 등의 많은 기능을 제공 할 수 없습니다. SQL은 그런 식으로 설계되지 않았으며 데이터 세트를 쿼리하는 언어이며 각 세트는 고유 한 방식으로 고유합니다.

즉, 조직 내에서 가독성이 우선 순위가 높은 경우 코드를 더 멋지게 보이게하는 방법이 있습니다. 자주 사용되는 SQL 블록 (사용하는 공통 데이터 세트)의 비트를 저장 프로 시저 / 테이블 값 함수에 저장 한 다음 임시 테이블 / 테이블 변수에 쿼리 및 저장 한 다음이를 사용하여 하나의 대규모 트랜잭션으로 조각을 결합합니다. 그렇지 않으면 작성하는 것이 옵션입니다. IMHO SQL로 그런 일을 할 가치가 없습니다.

언어는 프로그래머가 아닌 사람도 누구나 쉽게 읽고 이해할 수 있도록 설계되었습니다. 따라서 매우 영리한 작업을 수행하지 않으면 SQL 코드를 더 작은 바이트 크기 조각으로 리팩터링 할 필요가 없습니다. 개인적으로 데이터웨어 하우스 ETL /보고 솔루션에서 작업하는 동안 대량의 SQL 쿼리를 작성했으며 상황은 여전히 ​​명확했습니다. 다른 사람에게 조금 이상하게 보였던 것은 간단한 설명을 제공하기 위해 간단한 설명을 얻을 것입니다.

이게 도움이 되길 바란다.


6

귀하의 예에서 "하위 쿼리"에 중점을 두겠습니다.

왜 그렇게 자주 사용됩니까? 그들은 사람의 자연스러운 사고 방식을 사용하기 때문에 : 나는이 데이터 세트를 가지고 있으며, 데이터의 하위 세트에 대한 조치를 취하고 다른 데이터의 하위 세트와 결합하려고합니다. 하위 쿼리가 표시되는 10 번 중 9 번이 잘못 사용되었습니다. 하위 쿼리에 대한 저의 농담은 조인을 두려워하는 사람들이 하위 쿼리를 사용한다는 것입니다.

이러한 하위 쿼리가 표시되면 종종 최적화되지 않은 데이터베이스 디자인의 신호이기도합니다.

데이터베이스가 정규화 될수록 조인 수가 많을수록 데이터베이스가 큰 엑셀 시트처럼 보일수록 더 많은 하위 선택 항목이 나타납니다.

SQL의 리팩토링은 종종 다른 목표를 가지고 있습니다. 성능 향상, 쿼리 시간 단축, "테이블 스캔 피하기". 그것들은 코드를 읽기 어렵게 만들지 만 매우 가치가 있습니다.

그렇다면 왜 거대한 모 놀리 식 비 리팩터링 쿼리가 많은가?

  • 많은면에서 SQL은 프로그래밍 언어가 아닙니다.
  • 데이터베이스 디자인이 잘못되었습니다.
  • 사람들은 실제로 SQL에 유창하지 않습니다.
  • 데이터베이스에 전원이 공급되지 않음 (예 : 뷰를 사용할 수 없음)
  • 리팩토링과 다른 목표.

(나에게있어 SQL에 대한 경험이 많을수록 쿼리의 규모가 커질수록 SQL은 모든 기술 수준의 사람들이 업무를 중요하게 처리 할 수있는 방법을 갖습니다.)


6
"하위 쿼리는"몇 가지로 그냥 가능성이 집계 가 임시 정상화 비 표준화 된 DB의 수만큼 제대로 정규화 dB의
Caleth

@Caleth 정말 그렇습니다.
Pieter B

5
잘 표준화 된 데이터베이스에서도 테이블과 직접 조인하는 대신 하위 쿼리와 조인해야하는 경우가 종종 있습니다. 예를 들어 그룹화 된 데이터와 조인해야하는 경우
Barmar

1
@Barmar는 확실히 10 개 중 9 개입니다. 하위 쿼리는 그 자리를 차지하지만 경험이 부족한 사람들이 남용한 것으로 보입니다.
Pieter B

데이터베이스 정규화 (또는 부족)의 표시로 "하위 쿼리 수"에 대한 메트릭이 마음에 듭니다.
Jason

2

업무 분리

SQL 정신에서 데이터베이스는 회사의 데이터를 포함하는 공유 자산이므로이를 보호하는 것이 매우 중요합니다. 사원의 수호자로 DBA에 들어갑니다.

데이터베이스에서 새보기를 작성하는 것은 지속적인 목적을 제공하고 사용자 커뮤니티가 공유하는 것으로 이해됩니다. DBA보기에서 이는 데이터 구조에 의해보기가 정당화되는 경우에만 허용됩니다. 그런 다음 뷰를 변경할 때마다 현재 사용자, 심지어 응용 프로그램을 사용하지 않고 뷰를 발견 한 사용자에 대한 위험과 연결됩니다. 마지막으로, 새 개체를 만들려면 기본 테이블의 권한과 일관성있게 관리 권한을 관리해야합니다.

이 모든 것이 DBA가 일부 개별 애플리케이션의 코드를위한 뷰를 추가하는 것을 좋아하지 않는 이유를 설명합니다.

SQL 디자인

멋진 복잡한 쿼리 중 하나를 분해하면 하위 쿼리에 종종 다른 하위 쿼리에 의존하는 매개 변수가 필요하다는 것을 알 수 있습니다.

따라서 뷰에서 하위 쿼리를 변환하는 것이 명시된 것처럼 간단하지는 않습니다. 변수 매개 변수를 분리하고보기에서 매개 변수를 선택 기준으로 추가 할 수 있도록보기를 설계해야합니다.

불행하게도, 그렇게하면 때로는 맞춤형 쿼리보다 더 많은 데이터에 덜 효율적으로 액세스하도록 강요합니다.

독점 확장

PL / SQL 또는 T-SQL과 같은 SQL의 절차 적 확장으로 일부 책임을 이전하여 일부 리팩토링을 기대할 수 있습니다. 그러나 이들은 공급 업체에 따라 다르며 기술적으로 의존합니다. 또한 이러한 확장은 데이터베이스 서버에서 실행되므로 응용 프로그램 서버보다 확장하기가 훨씬 어려운 리소스에 더 많은 처리 부하가 발생합니다.

그러나 결국 문제는 무엇입니까?

마지막으로, 업무 분리와 SQL 디자인의 강점과 한계가 실제 문제입니까? 결국이 데이터베이스는 미션 크리티컬 환경을 포함하여 매우 중요한 데이터를 성공적이고 안정적으로 처리하는 것으로 입증되었습니다.

성공적인 리팩토링을 달성하려면 :

  • 더 나은 의사 소통을 고려하십시오 . DBA의 제약 조건을 이해하십시오. DBA에 데이터 구조에 의해 새로운 견해가 정당화되고,이 문제가 해결 방법이 아니며, 보안에 영향을 미치지 않는다는 것을 입증하면 확실히 작성에 동의 할 것입니다. 그러므로 그것은 공동의 관심사 일 것입니다.

  • 자신의 집을 먼저 청소하십시오 : 많은 곳에서 많은 SQL을 생성하도록 강요하는 것은 없습니다. 응용 프로그램 코드를 리팩터링하여 SQL 액세스를 분리하고 재사용 가능한 서브 쿼리를 제공하는 클래스 또는 함수를 작성하십시오 (자주 사용되는 경우).

  • 팀 인식 개선 : 애플리케이션이 DBMS 엔진으로보다 효율적으로 수행 할 수있는 태스크를 수행하지 않는지 확인하십시오. 올바르게 지적했듯이 절차 적 접근 방식과 데이터 지향적 접근 방식은 팀의 다른 구성원이 똑같이 마스터하지 않습니다. 배경에 따라 다릅니다. 그러나 시스템 전체를 최적화하려면 팀 전체를 이해해야합니다. 따라서 경험이 적은 플레이어가 휠을 재발 명하지 않고 DB 생각을 더 경험이 풍부한 회원과 공유하지 않도록 인식을 만듭니다.


여기에 +1 좋은 점이 있습니다. 일부 SQL이 얼마나 나쁜지 감안할 때, 뷰를 허용하기위한 DBA의 기억은 종종 완전히 이해할 수 있습니다. 또한 SQL은 리소스가 부족하거나 자주 실행되는 경우 피어 검토를 통해 이익을 얻을 수 있습니다.
로비 디

1

재점 1 & 3 : 뷰가 유일한 방법은 아닙니다. RDBMS에 따라 임시 테이블, 마트, 테이블 변수, 집계 열, CTE, 함수, 저장 프로 시저 및 기타 구성도 있습니다.

DBA (그리고 DBA와 개발자 모두라고 말하는 사람)는 세상을 꽤 이진적인 방식으로 보는 경향이 있기 때문에 성능 저하로 인해 견해 나 기능과 같은 것에 반대하는 경우가 많습니다.

결국 NF 관점 에서 차선책 임에도 불구하고 비정규 화 된 테이블 이 성능이 우수 하다는 인식으로 복잡한 조인의 필요성이 줄어 들었 습니다.

포인트 2에서 제기하는 LINQ 와 같은 기술을 사용하여 클라이언트 측에서 쿼리를 수행하는 경향도 있습니다 .

비록 SQL이 모듈화하는 데 어려움을 겪을 수 있지만 동의하지만 클라이언트 측 코드와 SQL 사이에는 항상 이분법이 있지만 4GL 은 다소 선을 흐리게 했음에도 불구하고 큰 발전이 이루어졌습니다 .

DBA / 건축가 / 기술 담당자가이 점에서 얼마나 기꺼이 감수 할 것인지에 달려 있다고 생각합니다. 조인이 많은 바닐라 SQL 이외의 것을 허용하지 않으면 큰 쿼리가 발생할 수 있습니다. 이 문제가 발생하면 머리를 벽돌 벽에 부딪치지 말고 에스컬레이션하십시오. 일반적으로 약간의 타협으로 작업을 수행하는 더 나은 방법이 있습니다. 특히 이점을 입증 할 수있는 경우에 더욱 그렇습니다.


1
"마트"구성에 대해 들어 본 적이 없습니다. 그게 뭐야?
주교

1
마트 는 리포지토리 (마스터 데이터베이스)의 하위 집합 일뿐입니다. 실행해야하는 특정 복잡한 쿼리가있는 경우 해당 요청을 처리하기 위해 특수 데이터베이스를 만들 수 있습니다. 가장 일반적인 예는보고 마트입니다.
로비 디

1
이것이 왜 다운 보트인지 혼란 스러웠습니다. 질문에 직접 대답하지는 않지만 "옵션 3 : 널리 사용되는 여러 가지 방법이 있습니다"라는 상당히 명확한 암시 적 대답을 제공합니다.
Dewi Morgan

데이터 마트에 대한 TIL. +1하세요!
주교
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.