1.쿼리를 작성할 때는 SELECT, FROM, WHERE, ORDER BY, LIMIT 등의 순서로 진행되며
작성한 쿼리는 쿼리편집기에서 정상적으로 작동하는지 테스트해볼 수 있다.
각 부위별로 확장이 가는한데
SELECT에는 반환 받고 싶은 필드들을 넣어줘야 하는데
SELECT Name, (SELECT LastName FROM Contacts) FROM Account처럼
하위 레코드를 가져올 수도 있다는 것을 볼 수 있고
기본적으로 id가 들어가기 때문에 결과는 id, 추가한 필드들의 목록으로 구성된 열과
결과에 맞는 레코드들이 담겨있는 것을 볼 수 있다.
FROM은 검색할 객체를 지정할 수 있다.
WHERE에서는 검색 조건을 지정할 수 있는데
예를 들어 아래의 경우 And와 OR이 사용된 것을 볼 수 있다.
WHERE (Name='SFDC Computing' OR (NumberOfEmployees>25 AND BillingCity='Los Angeles'))
AND는 소괄호로 감써져 있기 때문에 내부 AND조건이고
외부 OR조건이기 때문에 Name 조건 달성 또는
직원수 25+, City = LA의 두 가지 조건 달성일 경우라고 해석할 수 있다.
또한 와일드카드 ‘%’과 어중간한 ‘_’ 두 가지 조건이 추가로 존재하는데
문자열 비교를 할 때 LIKE를 사용해 비슷한 문자열을 찾을 수 있는데
WHERE Name LIKE 'SFDC%’의 경우 SFDC로 시작하는 문자열의 이름 모두 찾는 것이고
WHERE Name LIKE 'SFD_’의 경우 SFD로 시작하는 4글자의 문자열로 된 이름을 찾는 것이다.
ORDER BY는 정렬할 기준(Name 등)과 순서(default ASC)를 ASC, DESC로 나타낸다.
LIMIT은 최종적으로 표기하는 내용으로 몇 개의 자료를 출력할 것인지를 정할 수 있는데
이를 통해서 지나치게 많은 정보 표기를 제한하거나 출력 포맷에 맞출 수 있고
최종적으로는 아래와 같은 형태를 하게 된다.
SELECT Name,Phone FROM Account WHERE (Name = 'SFDC Computing' AND NumberOfEmployees>25) ORDER BY Name LIMIT 10
또한 결과를 저장할 때 from에 담긴 객체를 type으로 주면 되고
배열 형태의 경우 Account[]처럼 해당 객체가 담긴 배열 형태라고 알려줄 수 있다.
2.변수를 사용하고 싶은 경우 앞에 ‘:’기호를 사용해야 한다.
//WHERE (Name=:nameVar OR (NumberOfEmployees>:numVar AND BillingCity=:cityVar))
//FIND :soslFindClause
3.SOSL쿼리는 아래와 같은 형태로 작동하며
FIND 'SearchQuery' [IN SearchGroup] [RETURNING ObjectsAndFields]
쿼리 편집기에서는 SearchQuery 부분을 (’)가 아닌 ({})로 감싼다는 차이가 있다.
FIND에는 검색을 원하는 글자를 입력할 경우 찾아주지만
두 개 이상의 글자를 적은 경우 각각의 글자(aa, bb, cc)를 모두 포함된 레코드를 반환하는데 AND라고 볼 수 있다. 예를 들어 bbccaa라는 레코드가 존재하거나 aabb cc 라는 레코드가 존재할 경우에 조건이 달성된다.
기본 공백이 AND 처리라고 생각한다면 OR은 직접 적었을 경우 OR로 구분되는데
aa or bb로 입력한 경우 [abcd, aabc, abbc, abcc]가 존재한다고 했을 때
aa가 존재하는 aabc와 bb가 존재하는 abbc가 반환된다.
*라는 와일드카드가 존재하는데
aa*과 같은 형태로 입력할 경우 aa로 시작하는 모든 검색 결과를 반환하며
?는 해당 위치의 한 글자를 모를 때 사용한다.
String soslFindClause = 'Wingo OR SFDC';
List<List<sObject>> searchList = [FIND :soslFindClause IN ALL FIELDS
RETURNING Account(Name),Contact(FirstName,LastName,Department)];
Account[] searchAccounts = (Account[])searchList[0];
Contact[] searchContacts = (Contact[])searchList[1];
System.debug('Found the following accounts.');
for (Account a : searchAccounts) {
System.debug(a.Name);
}
System.debug('Found the following contacts.');
for (Contact c : searchContacts) {
System.debug(c.LastName +', ' + c.FirstName);
}
4.트리거는 아래와 같은 형태로 작성해야 한다.
trigger TriggerName on ObjectName (trigger_events) {
code_block
}
trigger_events에 들어갈 수 있는 목록은 아래와 같으며 “,”를 사용해 여러개를 감지할 수 있다.
- before insert - 삽입 전
- before update - 업데이트 전
- before delete - 삭제 전
- after insert - 삽입 후
- after update - 업데이트 후
- after delete - 삭제 후
- after undelete - 삭제 복원 후
트리거를 통해 발생한 레코드를 변경하기 위해서는 컨텍스트 변수를 사용할 수 있다.
trigger HelloWorldTrigger on Account (before insert) {
for(Account a : Trigger.new) {
a.Description = 'New description';
}
}
위에서 볼 수 있듯 a에 Trigger.new에 있는 객체를 할당했는데
이 객체는 before insert를 통해 받아온 Account 객체들을 Trigger.new로 받아오고
각 객체를 할당받은 a의 필드를 .fieldName을 통해 지정한 다음 바로 변경시켜버렸다.
Trigger.xxx를 통해 받아온 컨텍스트 변수들은 DML을 사용하면 오류가 발생하며
trigger가 끝난 이후 자동으로 변경된 값이 반영되기 때문에 직접적으로 값을 수정해줘야한다.
Trigger와 사용할 수 있는 모든 컨텍스트 변수의 목록으로 주 사용은 new, old, isXXX, size 정도가 있는 것 같다.
| 변수 | 사용 |
| isExecuting | Apex 코드의 현재 컨텍스트가 Visualforce 페이지, 웹 서비스 또는 executeanonymous() API 호출이 아닌 트리거인 경우 true를 반환합니다. |
| isInsert | Salesforce 사용자 인터페이스, Apex 또는 API에서 삽입 작업으로 인해 이 트리거가 실행된 경우 true를 반환합니다. |
| isUpdate | Salesforce 사용자 인터페이스, Apex 또는 API에서 업데이트 작업으로 인해 이 트리거가 실행된 경우 true를 반환합니다. |
| isDelete | Salesforce 사용자 인터페이스, Apex 또는 API에서 삭제 작업으로 인해 이 트리거가 실행된 경우 true를 반환합니다. |
| isBefore | 레코드가 저장되기 전에 이 트리거가 실행된 경우 true를 반환합니다. |
| isAfter | 모든 레코드가 저장된 후에 이 트리거가 실행된 경우 true를 반환합니다. |
| isUndelete | Recycle Bin(휴지통)에서 레코드를 복구한 후 이 트리거가 실행된 경우 true를 반환합니다. 이 복구는 Salesforce 사용자 인터페이스, Apex 또는 API에서 삭제 취소 작업 후에 발생할 수 있습니다. |
| new | sObject 레코드의 새 버전 목록을 반환합니다. 이 sObject 목록은 insert, update 및 undelete 트리거에서만 사용할 수 있으며 레코드는 before 트리거에만 수정할 수 있습니다. |
| newMap | sObject 레코드의 새 버전에 대한 ID 지도입니다. 이 지도는 before update, after insert, after update 및 after undelete 트리거에서만 사용할 수 있습니다. |
| old | sObject 레코드의 이전 버전 목록을 반환합니다. 이 sObject 목록은 update and delete 트리거에서만 사용할 수 있습니다. |
| oldMap | sObject 레코드의 이전 버전에 대한 ID 지도입니다. 이 지도는 update 및 delete 트리거에서만 사용할 수 있습니다. |
| operationType | 현재 작업에 해당하는 System.TriggerOperation 유형의 열거형을 반환합니다. System.TriggerOperation 열거형의 가능한 값은 다음과 같습니다. BEFORE_INSERT, BEFORE_UPDATE, BEFORE_DELETE, AFTER_INSERT, AFTER_UPDATE, AFTER_DELETE, 및 AFTER_UNDELETE. 다양한 트리거 유형에 따라 프로그래밍 논리를 변경하는 경우 고유한 트리거 실행 열거 상태의 순열이 다른 switch 문을 사용해 보세요. |
| size | 이전 및 새 트리거 호출의 총 레코드 수입니다. |
결국 리액트에서 컴포넌트를 재사용하듯 클래스를 재사용 할 수 있다는 것을 볼 수 있고
바로 .sendMail을 사용할 수 있는 이유는 static으로 선언했기 때문이니
평소에도 편리한 재사용을 위해 static으로 생성하는 버릇을 들여야 할 것 같다.
또한 위 코드에서는 Trigger의 isInsert, isDelete가 포함된 예시를 볼 수 있는데
해당 코드처럼 is가 들어간 경우 boolean type으로 return되기 때문에 조건문에 사용하기 좋다.
5.위에서 언급헀던 것 처럼 컨텍스트 변수를사용해 for문으로 할당하는 것으로
대량 SOQL을 효과적으로 수행할 수 있기 때문에
for문 내부에서 SOQL을 다시 실행해 문제를 발생시키지 않고
위의 예제와 유사하게 for(objectName varName : Trigger.new){} 형태 내부에서 varName을 다뤄야 한다.
구조가 복잡할수록 잘못된 설계를 통한 SOQL 반복요청이 생길 위험이 커지는데
아래와 같이 사용하고 싶은 객체를 먼저 SOQL 요청을 생성한 다음
해당 SOQL을 반복문으로 수정해야 한다.
(단 컨텍스트 변수로 작업하는 것이 아니기 때문에 직접 변경이 아닌
아래처럼 해당 객체에 재할당 후 추가적인 DML 작업을 해야 한다.
trigger SoqlTriggerBulk on Account(after update) {
// Perform SOQL query once.
// Get the accounts and their related opportunities.
List<Account> acctsWithOpps =
[SELECT Id,(SELECT Id,Name,CloseDate FROM Opportunities)
FROM Account WHERE Id IN :Trigger.new];
// Iterate over the returned accounts
for(Account a : acctsWithOpps) {
Opportunity[] relatedOpps = a.Opportunities;
// Do some other processing
}
}
trigger SoqlTriggerBulk on Account(after update) {
List<Opportunity> relatedOpps = [SELECT Id,Name,CloseDate FROM Opportunity
WHERE AccountId IN :Trigger.new];
for(Opportunity opp : relatedOpps) {
}
}
/** 위 아래가 같은 코드로 길이만 단축된 상태 */
trigger SoqlTriggerBulk on Account(after update) {
for(Opportunity opp : [SELECT Id,Name,CloseDate FROM Opportunity
WHERE AccountId IN :Trigger.new]) {
}
}
6.DML 수행 또한 대량으로 할 경우 150회의 호출 제한이 있기 때문에 한번에 처리해야 하며
한번에 처리하는 것이 속도적으로도 효율적이다.
마치 console.log()로 대량의 개별적인 출력을 for문 내부에서 하는 것이 아니라
const result = [] / result.push(data) / console.log(result.join(’\n’)) 과 같은 느낌이라고 볼 수 있다.
예제로 작성된 대량 DML 코드는 다음과 같다.
trigger DmlTriggerBulk on Account(after update) {
// Get the related opportunities for the accounts in this trigger.
List<Opportunity> relatedOpps = [SELECT Id,Name,Probability FROM Opportunity
WHERE AccountId IN :Trigger.new];
List<Opportunity> oppsToUpdate = new List<Opportunity>();
// Iterate over the related opportunities
for(Opportunity opp : relatedOpps) {
// Update the description when probability is greater
// than 50% but less than 100%
if ((opp.Probability >= 50) && (opp.Probability < 100)) {
opp.Description = 'New description for opportunity.';
oppsToUpdate.add(opp);
}
}
// Perform DML on a collection
update oppsToUpdate;
}
7.아래와 같이 과제를 수행했다.(셀프 참고용)
trigger ClosedOpportunityTrigger on Opportunity (after insert, after update) {
List<Task> taskList = new List<Task>();
for(Opportunity opp : Trigger.new) {
if(Trigger.isInsert) {
if(Opp.StageName == 'Closed Won') {
taskList.add(new Task(Subject = 'Follow Up Test Task', WhatId = opp.Id));
}
}
if(Trigger.isUpdate) {
if(Opp.StageName == 'Closed Won'
&& Opp.StageName != Trigger.oldMap.get(opp.Id).StageName) {
taskList.add(new Task(Subject = 'Follow Up Test Task', WhatId = opp.Id));
}
}
}
if(taskList.size()>0) {
insert taskList;
}
}
//1.Opportunity.Stage를 받기 위해 구문생성
//2.CLosed Won과 비교
//3.task 만들기?? [Subject: Follow Up Test Task, WhatId: the opportunity ID]
//4.대량처리를 위한 task배열 만들기
//5.길이체크
(1).백준 2747번 피보나치 수는 이름 그대로 피보나치를 구하는 문제였다.
dp에서 주로 다루기는 하지만 재귀가 아닌 bottom-up 방식으로 해결했다.
const input = 10
const fibo = [0,1]
for(let i = 2 ; i <= input ; i++){
fibo[i] = fibo[i-1] + fibo[i-2]
}
console.log(fibo[input]'회고' 카테고리의 다른 글
| [수습일지] - 18 (0) | 2023.04.13 |
|---|---|
| [수습일지] - 17 (0) | 2023.04.12 |
| [수습일지] - 15 (0) | 2023.04.10 |
| [수습일지] - 14(주말) (0) | 2023.04.09 |
| [수습일지] - 13(주말) (0) | 2023.04.08 |
