다음은 새로운 버전의 코드입니다. 내가 해낸 방식이 조금 지저분하지만 포인트가 나오는 부분에 설명과 설명을 넣었는지 이해하는 것이 가장 쉽다고 생각했습니다.
첫 번째로 말하지만 지정된 행에 대해 Null 인 데이터 집합 필드를 확인하는 것은 좋지만 데이터베이스가 쿼리 할 수있는 열에 Null을 저장할 수 없다면 더 좋습니다. 응용 프로그램 또는 원시 SQL 쿼리에 의해. Nulls가 이론적 및 실용적인 측면에서 의미하거나하지 않는 것에 대해 전체 장을 썼습니다. 실제로, 그들은 보통 "정보 누락"을 의미하는 것으로 가장 잘 생각됩니다. 그래서 우리는 "이 사용자는 개를 원하나요?"라는 질문에 대답 할 수 없습니다. Null이있는 상태에서. 따라서 Null과 관련된 정책 및 디자인 선택의 문제이지만 열에 NOT NULL 제약 조건을 적용하여 데이터베이스 구현시 문제가 결정되는 것이 일반적으로 훨씬 낫습니다.
델파이가 Null과 빈 필드의 차이를 존중하는 데 특히 좋지 않다는 의견에 대해 말씀 드렸을 때 나는 무엇을 의미합니까? 문자열 필드의 경우 필드가 Null 인 행의 경우, Delphi는 Field.AsString을 호출 할 때 빈 문자열 ''을 반환합니다. 일부 "순정 주의자"는 Null을 포함 할 때 AsXXX 속성을 요청하면 TField에서 예외를 생성해야한다고 말합니다. 실제로 값이없는 경우 값을 "가짜"로해서는 안되기 때문입니다. 대신에 빈 문자열, 숫자 필드에 0을 반환하는 등의 작업은 약간의 실용적인 절충안입니다. 초보자가 Null이 있다는 사실을 알지 못하도록하지만, 코드에서 Null을 처리하도록하려면 다음을 수행 할 수 있습니다. TField.IsNull을 사용하십시오.
Null이 포함 된 DB에 익숙하지 않은 경우 - 자주 DB를 디자인하지 않는 한 우울하게 자주 사용됩니다. - SQL을 처리하는 데 가장 적합한 SQL 문을 사용할 가능성을 고려하십시오 귀하의 델파이 코드 전에 데이터를 볼 수 있습니다.
두 번째로 명확한 디자인 요약이 없으면 정확히 "개를 원한다"는 것을 정확히 알지 못합니다. 예를 들어 사용자가 개를 단수, 복수 또는 예/아니요 갖고 싶다는 뜻입니까? 예/아니오는 처리가 가장 간단하지만 모든 데이터베이스가 명시 적 부울 열 유형을 지원하는 것은 아니므로 자주 사용되는 (잘만하면 단일 문자) CHAR, VARCHAR (또는 해당 유니 코드 등가) 열이 대신 사용됩니다. 또는 사용자의 까다로운 요구 사항을 충족해야한다면 정수 열.
세 번째 사항은 필드의 델파이 데이터 유형이 반드시 db의 열 유형과 완전히 동일하지는 않지만 두 필드 사이에 표준 매핑이 있지만 일반적으로 값으로 델파이 코드에서 작업하는 것이 가장 좋습니다 db 컬럼 유형과 가장 잘 일치하는 표현 (예 : .AsString, .AsInteger, .AsFloat).
물론 모든 데이터베이스 구현이 까다 롭지는 않습니다. 죄송합니다. 델파이가 열의 데이터 유형을 처리하고 있지만 대부분주의해야합니다. 주목할만한 예외는 Sqlite입니다. 여기서는 테이블의 DDL에서 열 유형을 정의 할 수 있지만 Sqlite 엔진은 규칙보다 더 많은 권장 사항을 처리하므로 거의 모든 것을 저장할 수 있습니다.
앞서 언급 한 것처럼 널 (Null)은 값이 아닌 상태 인입니다. 그래서 "어떤 데이터 유형이 빈 DB 필드입니까?" 다양한 "범주 오류"입니다. 열의 데이터 유형은 "주석에서 말하는 것과 정확히 일치"합니다. 즉 테이블의 DDL에 정의 된 내용입니다. 실제로 묻고 있던 것은 "필드가 비어 있는지 어떻게 판단 할 것인가"이며 이는 델파이 코딩과 마찬가지로 설계 선택과 db 구현의 문제입니다. 일반적 충분히 어쨌든
, ...
procedure CountDogsWanted;
var
// i,k : Integer ; <- names like i and k are usually as used for loop variables
DogsWanted : Integer;
Wanted : Boolean;
S : String; // contrast this naming style with what I said about the likes of i, j, k
// I've done this because we might want to do several tests & operations on its value
// and they will be easier to read with a shorter variable name. Not such a good idea
// when there are several such variables.
AField : TField;
const
scDogsWanted = 'Dogs wanted'; // this is to avoid making mistakes with typos
begin
{i := 0 ;}
DogsWanted := 0;
// The point of the following line is to retrieve the field we're working with
// only once, rather than doing a FieldByName (which involves a serial iteration
// through the dataset's Fields collection) for each row in the dataset.
// The AField variable will remain valid for the duration of this procedure
// or until the dataset is closed if its Fields aren't defined as persistent ones.
// Persistent fields remain valid for the lifetime of their owners (usually a
// datamodule or form). OTOH, "dynamic" fields are owned by the dataset and created
// and destroyed when the dataset is opened and closed.
AField := dmregisteredusers.tblusers.FieldByName(scDogsWanted);
{with dmregisteredusers do <- Don't use "with", it only ever causes problems}
{begin}
{tblusers.Sort := 'Nommer ASC'; <- pointless, it makes no difference when you count what order the things are in}
{tblusers.Edit; <- No! This puts the table into Edit state, but there's not point because you're not changing anything, and in any case, it will drop out of Edit state when you do a .Next}
dmregisteredusers.tblusers.First;
while not dmregisteredusers.tblusers.Eof do
{For k:= 1 to tblusers.RecordCount do <- Never, ever, iterate a dataset with a For loop.
The RecordCount is best avoided, too, because not all types of dataset return a meaningful
RecordCount }
begin
// You need to decide what to do about users whose 'Dogs wanted' field is Null
// If is is Null maybe we should ask for the record to be changed to indicate
// explicitly whether a dog is wanted or not
if AField.IsNull then begin
// to be filled in by you
end;
// You haven't told us what DataType the 'Dogs wanted' field is
// There are several possibilities. For simplicity, let's assume that the DataType of the field is ftString
// Let's also make a design decision that *only* if the field only contains a 'Y' in upper or lower
// case, then that means a dog is wanted.
// First copy the field's string value into a local variable so we don't have to keep getting it for the
// following operation;
S := dmregisteredusers.tblUsers.FieldByName(scDogsWanted).AsString;
S := Trim(S); { Trim() is a statndard function which removes leading and trailing whitespace }
Wanted := CompareText(S, 'Y') = 0; { CompareText does a case-insensitive comparison and returns zero if equal}
If
{ (tblusers['Dogs wanted'] = '') OR (tblusers['Dogs wanted'] = ' ')
OR (tblusers['Dogswanted'] = 0)}
Wanted
then
{tblusers.Next <- this is in the wrong place, you want to do a .Next regardless of the outcome of the "if"}
else
begin
inc(DogsWanted);
{tblusers.Next;}
end;//else
dmregisteredusers.tblusers.Next;
{end;//with}
end;//for
ShowMessage('There are ' + IntToStr(DogsWanted) + ' new dogs added to wishlist ,please contact the users regarding this matter and them remove the dogs from their wishlist !')
end;
['IsNull'] (http://docwiki.embarcadero.com/Libraries/XE6/en/Data.DB.TField.IsNull) – TLama
세 번째 '개'다음에 공백이 누락 된 것처럼 보입니다. 오식? 어쨌든, "tblusers [ 'Dogs wanted'] 구조를 사용하면 필드의 내용에 변형이있는 경우 (OLH 참조) 액세스하고 있으며, 현재 이해할 수있는 깊이가 너무 멀다고 생각합니다. 현재와 같은 작업으로 인해 발생할 수있는 문제. 필드에 들어가려면 tblusers.FieldByName ('Dogs wanted')을 사용하십시오. DataType 속성은 어떤 유형인지 알려줍니다. Btw, 절대 "For"루프를 사용하여 데이터 집합을 반복합니다. "동안 tblusers.Eof 할 ..."하나를 사용하십시오. 그리고 어떤 데이터베이스 유형을 사용하고 있습니까? – MartynA
또한 Delphi는 데이터 세트의 주어진 열의 데이터 유형을 테이블의 각 행에 대해 동일하게 취급합니다. Null은 * 값이 아닌 * 상태 *입니다. 즉, 열 (필드)에 해당 행의 값이 없음을 의미합니다. 누구든지 프로젝트를 설정해야하고, 빈 <> NULL이라는 사실을 설명해야합니다. 그것은 중요한 차이입니다. 가운데 이름에 대한 열이 있는데 그 중 하나가없는 경우 공백 ('') 또는 NULL로 필드를 사용해야합니까? 델파이는 실제로이 차이점을 존중하는데 특히 좋지는 않지만 그것에 대해 분명히해야합니다. – MartynA