乱数を振り当てランダムにある一定数のデータを取得するには?

解決


りな  2010-01-02 11:11:41  No: 36625  IP: 192.*.*.*

delphi4 servicepack3  database:oracle 8iを使用しています。
今、ランダムに数字をデータに振り分け、その中で数字の5なら5とその数をもつデータを指定のかず分抽出する方法を試しています。ただ、非常に複雑な構文のため、なかなかうまく機能しません。たびたびすみませんが、お力を貸していただけないでしょうか?

流れとしては、まずテーブルに乱数をいれるための項目と、乱数取得フラグ項目を設置する→クエリを開いて、乱数を入れるための項目に乱数を振り当てる→乱数で数字の5が振り当てられたデータに対して乱数フラグに1を立てる→それをテーブルにあるデータすべてに対して行う(テーブルデータの最後まで)→その処理を指定したデータ数(30なら30個分のデータ)だけ取得するまで行う→最後に抽出する。

下記のように、プログラムを組んでみました。恐れ入りますが、どう修正すればいいか詳しく指摘していただけないでしょうか(別に、もっとシンプルにできる方法をご存知でしたら、教えていただければ幸いです)?

begin
   query.sql.clear;
   query.sql.open;
   begin
     repeat
      while not query.EOF do
      begin
         randomize;
         s_randomCode := random(10) + 1;
         with queryA do
         begin
           sql.add('Update tableA set randomCD = :value1');
           sql.add('where randomFLG <> 1');
           prepare;
           params[0].AsString  := s_randomCode;
            if s_randomCode = 5 then
             begin
              count := count + 1;
              sql.add('update tableA set randomFLG = 1');
             end;
           next;
         end;  
      end;
     until count = StrToInt(number.Text);
   ExecSql;
  end;
end;

編集 削除
igy  2010-01-02 15:13:59  No: 36626  IP: 192.*.*.*

>下記のように、プログラムを組んでみました。

このコード、コンパイルでエラーが出ません?

あと、やりたいことがよく分かりません。
(私に読解力がないからかもしれませんが・・)

編集 削除
りな  2010-01-02 15:45:15  No: 36627  IP: 192.*.*.*

エラーになるのですが、正直どう修正すればいいのかわからない状態です。


>あと、やりたいことがよく分かりません。

やろうとしていることは、抽選みたいなものです。たとえば、1000人ある商品の抽選に対して応募があったとき、その1000人の中からランダムに30人の当選者を選ぶという作業です。
ですので、わたしはまず1000人文のデータに1つ別の項目を作成して、乱数を振り当て、その中である特定の数(ここでは、数字の5)の数を振り当てられた方を当選者として、抽出しようとしています。ただ、乱数なのでいつ数字の5がでてくるかわからないので、何度かデータを最初のレコードから最後のレコードまで、同様の処理を繰り返すように、上記のようなプログラムを組んでみました。いちど、5が割り当てられたデータは、二回目にデータを読み込むときにはじかれるようにしたいと思っています。

説明が不十分で申し訳ありません。

編集 削除
igy  2010-01-02 16:05:15  No: 36628  IP: 192.*.*.*

Oracleで全レコードからランダムに行を取得
http://d.hatena.ne.jp/Kiske/20070524/1179967113

とかではだめですか?

編集 削除
りな  2010-01-02 22:35:28  No: 36629  IP: 192.*.*.*

igyさん

教えてくださったサイト上に記載されている構文(以下に記述)のdbms_randomというのは何を指しているかお分かりになりますか?

SAMPLE使わない方法 SELECT WORD
FROM  (SELECT dbms_random.random() as rand, WORD FROM TEST ORDER BY rand)  WHERE ROWNUM <= 10;

編集 削除
igy  2010-01-02 22:53:48  No: 36630  IP: 192.*.*.*

>dbms_randomというのは何を指しているかお分かりになりますか?

Oracleは使ったことがないので、わからないです。

Googleとかで検索してみるのは、いかがですか?

編集 削除
りな  2010-01-03 08:21:17  No: 36631  IP: 192.*.*.*

ランダム行を取得して、取得した行の項目(当選者:elected)に1を代入するという処理ですが(tableAはテーブル名、electedとA_IDはそれぞれtableAの項目)、

with query do
 begin
  close;
  SQL.ADD('Update TableA set elected = 1');
  SQL.ADD('Where Select A_ID From(Select dbms_random.random() as rand, A_ID FROM TableA ORDER BY rand);
  end;
 ExecSql
end;

という書き方で、うまくいきません。お分かりになられる方がいらっしゃいましたら、恐縮ですが、教えていただけないでしょうか?

よろしくお願い致します。

編集 削除
igy  2010-01-03 08:29:00  No: 36632  IP: 192.*.*.*

うまくいきません。
>うまくいきません。

具体的に、どのようにうまくいかないのか、書いたほうがよいかと思います。

>  SQL.ADD('Where Select A_ID From(Select dbms_random.random() as rand, A_ID FROM TableA ORDER BY rand);

これ、コンパイルでエラーが出ません?

末尾のrandの後ろに')がないので・・・

編集 削除
igy  2010-01-03 08:38:31  No: 36633  IP: 192.*.*.*

訂正です。
×:末尾のrandの後ろに')
○:末尾のrandの後ろに)'

編集 削除
igy  2010-01-03 08:52:50  No: 36634  IP: 192.*.*.*

あと、ExecSqlは、
with query do
 begin
 end;
の中に移動したほうがよいかもしれません。

# beginが1つなのにendが2つあるのも気になりますが・・

編集 削除
igy  2010-01-03 09:01:53  No: 36635  IP: 192.*.*.*

あと、
close;
の下に
SQL.clear;
が必要かもしれません。

編集 削除
D  2010-01-03 13:01:48  No: 36636  IP: 192.*.*.*

データベースはまったく分からないのですが。
1000人の応募者の中から30人を選ぶということならば、TStringListのDuplicatesプロパティを利用する方法もあるかと思います。


function SortFunc(List: TStringList; Index1, Index2: Integer): Integer;
var
  li_Num1, li_Num2: Integer;
begin
  li_Num1 := StrToInt(List[Index1]);
  li_Num2 := StrToInt(List[Index2]);
  if (li_Num1 > li_Num2) then begin
    Result := 1;
  end else if (li_Num1 < li_Num2) then begin
    Result := -1;
  end else {if (li_Num1 = li_Num2) then} begin
    Result := 0;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
const
  lci_ENTRY = 1000;
  lci_LOT   = 30;
var
  lsl_List: TStringList;
  ls_Index: String;
begin
  Randomize;

  lsl_List := TStringList.Create;
  try
    //重複を許可しない
    lsl_List.Sorted     := True;
    lsl_List.Duplicates := dupIgnore;

    repeat
      ls_Index := IntToStr(Random(lci_ENTRY-1)); //0〜999
      lsl_List.Add(ls_Index);
    until (lsl_List.Count >= lci_LOT);

    //リストを番号順に並べ替え
    lsl_List.Sorted := False;
    lsl_List.CustomSort(SortFunc);

    //lsl_Listには30人分のインデックスが番号順に入っているので
    //StrToIntで数値にしてデータベースから取り出す
  finally
    lsl_List.Free;
  end;
end;

編集 削除
HOta  2010-01-04 11:47:05  No: 36637  IP: 192.*.*.*

ランダムから値を得る部分と、データーベースから選択する部分は、別にしたほうが、後から判りやすくなりますよ。別の処理をひとつにするのは、エラーのときなどにわかりにくくなります。

編集 削除
りな  2010-01-05 22:01:21  No: 36638  IP: 192.*.*.*

取得までは以下の方法でうまくいったのですが、

sql.add('select * from tableA sample(50) where rownum <= 3');

取得したデータの1つの項目を、updateしてそのデータに抽選されたフラグを立てたいのですが、以下のように書いても、SQL構文エラーが出るか、または実行しても全く反応しません。どのように、命令文を書けばよいでしょうか?

with query do
 begin
 close;
 sql.add('update tableA set FLAG = 1');
 sql.add('where select * from tableA sample(50) where rownum <= 3');
 execsql;
 end

編集 削除
igy  2010-01-05 23:50:55  No: 36639  IP: 192.*.*.*

SQLはあまり詳しくないのですが、
プライマリーキーが HOGE_ID とすると、

update tableA set FLAG = 1
where HOGE_ID IN (select HOGE_ID from tableA sample(50) where rownum <= 3)

みたいにするのは、いかがですか?

編集 削除
HOta  2010-01-06 14:32:30  No: 36640  IP: 192.*.*.*

>sql.add('select * from tableA sample(50) where rownum <= 3');
でどのような結果が得られるのでしょうか?
select * from tableA where rownum <=3
ではないでしょうか?
これならば、
update tableA set
   FLAG = 1
 where rownum <=3
となります。

編集 削除
manbon  2010-01-06 16:05:14  No: 36641  IP: 192.*.*.*

あまり読んでいないのですが、
重複することなく、1〜1000の中から、30個抽出する
という問題と考えていいのでしょうか?

編集 削除
りな  2010-01-06 22:50:32  No: 36642  IP: 192.*.*.*

はい、そうです。  manbonさんがおっしゃったとおり、たくさんあるデータの中から(たとえば、1000件)、ランダムに30件なら30件とデータを抽出することです。
以下のようにしたら、抽出まですることができました。
add('select * from tableA sample(50) where rownum <= value1');
prepare;
params[0].AsString := 変数;  ←TEditで件数を指定できるようにしている。

ただ、これをupdateのwhereの部分で指定して、データベース上の1つの項目に抽出したしるしとして、フラグを立てるようにしたいのですが、うまくいかない状態です(コンパイルエラーになってしまいます)。アドバイスいただけないでしょうか? 以下に、エラーになってしまう構文を書きます。
sql.add('update tableA set FLAG = 1');
sql.add('where ID IN (select ID from tableA sample(50) where rownum <= ;value1');
prepare;
(略)
execsql;

編集 削除
igy  2010-01-07 00:03:24  No: 36643  IP: 192.*.*.*

>sql.add('where ID IN (select ID from tableA sample(50) where rownum <= ;value1');
は、
sql.add('where ID IN (select ID from tableA sample(50) where rownum <= :value1)');
だと思いますが、

>コンパイルエラーになってしまいます

は、別の箇所が原因じゃないでしょうか?

編集 削除
HOta  2010-01-07 12:44:05  No: 36644  IP: 192.*.*.*

コンパイルエラーと実行時エラーは分けて書いてください。
>sql.add('update tableA set FLAG = 1');
>sql.add('where ID IN (select ID from tableA sample(50) where rownum <= ;value1');
はigyさんも書かれていますが、「)」が足りません。また、parameter変数の前は「;」ではなく「:」です。
SQLPlusで
update tableA set FLAG = 1
where ID IN (select ID from tableA sample(50) where rownum <= 変数)
ではできませんか?(変数は実際の値を入れてください。)

編集 削除
りな  2010-01-07 15:30:19  No: 36645  IP: 192.*.*.*

SQLでHotaさんがしていした構文を右かっこがありませんというエラーがでてきました。

delphi上でも、同じエラーがでました。

編集 削除
HOta  2010-01-07 17:11:09  No: 36646  IP: 192.*.*.*

igyさんの書いてあるSQLでもエラーは出ますか?

編集 削除
Ru  2010-01-07 17:21:01  No: 36647  IP: 192.*.*.*

実際に実行しているSQLを掲示してもらえないでしょうか?

1.execsql の直前の行に showmessage(sql.text)を記載
2.ダイアログボックス表示状態で Ctrl + C でコピー

編集 削除
りな  2010-01-07 23:30:13  No: 36648  IP: 192.*.*.*

はい、sql.add('where ID IN (select ID from tableA sample(50) where rownum <= :value1)');  
このように書いても、右かっこが足りないというエラーが出てしまいます。
select文では、うまくランダム行を取得できているので、なにか抽出した後、別の方法でupdateのような処理はできないのか考えているのですが、なかなかいいものが思い浮かびません。

編集 削除
GTR  2010-01-08 13:17:09  No: 36649  IP: 192.*.*.*

UPDATE,insertなどで副問合わせにSample()は使えなかったと思います。
Selectは大丈夫だけどね。

これでどうかな?
UPDATE TABLE1 
SET FLAG = '1'
Where to_number(ID) in
(Select Trunc(dbms_random.value*1000) as rand from TABLE1 where rownum <= 30)

編集 削除
りな  2010-01-09 14:13:41  No: 36650  IP: 192.*.*.*

GTRさん、指定していただいた文をdephi上でかくとどのようになりますか?いろいろと試してみたのですが、コンパイルエラーになかったり、実行しても何も反応がなかったりと、うまく機能させることができません。
私の場合ですと、データの件数は随時追加されていくので、1000件など決まってなく、また抽出する件数とある一定の数字が項目に入っているデータを抽出しようとしているので、2つ変数を使っています(抽出件数s_number,ある数字が入っている項目s_koumokuという変数を使っています)。
あと、value*1000のところもどうすればいいのかアドバイスいただけないでしょうか?


begin
 s_number := editNUMBER.Text;
 s_koumoku := editKOUMOKU.Text;
 with query1 do
  begin
  sql.clear;
  sql.add('update TableA set FLAG = '1' where ID in( Select trunc (dbms_random.value) as rand from TableA where rownum <= :value1)and  KOUMOKU = :value2');
prepare;
params[0].AsString := s_number;
params[1].AsString := s_koumoku;
execsql;
end;
end

編集 削除
りな  2010-01-09 14:15:42  No: 36651  IP: 192.*.*.*

追加で、お伺いしたいのですが、
一度、select文とsample関数でランダムにデータを抽出した後に、DBGridなどを使って、その抽出データにflgを立てるような別の方法はないのでしょうか?

編集 削除
HOta  2010-01-09 17:10:17  No: 36652  IP: 192.*.*.*

GTRさんの

UPDATE TABLE1 
SET FLAG = '1'
Where to_number(ID) in
(Select Trunc(dbms_random.value*1000) as rand from TABLE1 where rownum <= 30)

をOracleのSQL Plusで実行した場合は、実行できますでしょうか?
実行できたのなら、この更新SQLをDelphiで書いた場合。

begin
 s_number := editNUMBER.Text;
 s_koumoku := editKOUMOKU.Text;
 with query1 do
  begin
  sql.clear;
  sql.add('Update Table1');
  sql.add('Set FLAG = ''1''');
  sql.add('Where to_number(ID) in');
  sql.add('(Select Trunc(dbms_random.value*1000) as rand from TABLE1 where rownum <= :value1)');
  sql.add('  and  KOUMOKU = :value2');
>  params[0].AsString := s_number;
上のSQL文から判断すると、rownumは整数型だと思えますので、
  params[0].AsInteger := StrToIntDef(s_number,0);
  params[1].AsString := s_koumoku;
  execsql;
 end;
end
となります。


TDBGridのOptionのdgRowSelect=True,dgMultiSelect=Trueとして、
複数行を選択した場合の処理。

var   //変数宣言
  I : Integer;


with DBGrid1 do
  for I := 0 to SelectedRows.Count - 1 do
  begin
    GotoBookmark(pointer(SelectedRows.Items[I]));
    //DBGrid1のDatasetに対するFlgを立てる処理
  end;

編集 削除
りな  2010-01-09 20:26:15  No: 36653  IP: 192.*.*.*

HOtaさん、いつも詳しい情報本当にありがとうございます。

SQLplusの調子が悪く,GTRに教えていただいたことを試せていない状態です。復旧次第すぐに試してみます。

value*1000について、おそらくこれはvalueのままですと0〜1の浮動小数点しか返さないので、データ数が1000件の場合を想定して、1000をかけていると思うのですが、データの件数が決まっていない場合は、実行時のデータの件数を取得して、変数に代入・そしてvalue*変数の方法しかないのでしょうか?

編集 削除
りな  2010-01-09 20:38:03  No: 36654  IP: 192.*.*.*

HOtaさん、DBGridのflg(フラグ)を立てるという処理ですが、説明不足でした。申し訳ありません。私が、flgを立てるといった意味は、今までのupdate処理と同じで、抽出されたデータベースのテーブルの1つの項目に1というフラグを立てるということです。select文ではsample関数が使えるので、抽出したDBGrid上のデータに、何かしら命令文を使って、データベース上のテーブル項目に1を入れることはできる方法はないのかなと思い、お伺いしました(たとえば、update文とDBGridを組み合わせて、1をいれる)。

編集 削除
HOta  2010-01-10 09:05:54  No: 36655  IP: 192.*.*.*

はい。乱数をOracleから得てその行数に基づいて間作するのでしたら、この方法になります。乱数をDelphi側で作れば、その乱数に応じて選択すればよいことです。

GotoBookmarkは、DataSetのレコードを選択行に移しますから、
Update文のWhere句で
SQL.Add('Where ID = ' + Dataset.FieldByName('ID').asString);
でSQL文を作れば該当行に対して更新できます。

編集 削除
りな  2010-01-12 10:13:57  No: 36656  IP: 192.*.*.*

GTRさんから教えて頂いた文のSQL PLUSでためしたのですが、微調整しても必ず、0行更新と表示され、1行も更新されません。
試したコマンドは、VALUE*10(データ10件で試しているので)の以外全くGTRさんのと同じです。

恐縮ですが、アドバイス頂けないでしょうか?

編集 削除
HOta  2010-01-12 11:27:13  No: 36657  IP: 192.*.*.*

Select Trunc(dbms_random.value*1000) as rand
 from TABLE1
 where rownum <= :value1
で選択結果は有りますか?
有るならば、
Select *
from Table1
where ID in (
Select Trunc(dbms_random.value*1000) as rand
 from TABLE1
 where rownum <= :value1
)
で選択は出来ますか?
選択行が有れば、該当行に対してUpdateは可能ですが、無ければ0行更新になります。

編集 削除
りな  2010-01-12 11:54:47  No: 36658  IP: 192.*.*.*

ひとつ目のSELECT文はレコードが選択されたのですが、ふたつ目の長いSELECT文は「レコードが選択されませんでした」と表示されました。つまり、これはこの方法で実行することができないということでしょうか?

編集 削除
GTR  2010-01-12 13:35:09  No: 36659  IP: 192.*.*.*

「レコードが選択されませんでした」とのことですが
>where ID in (Select Trunc(dbms_random.value*1000) as rand
IDは数値ですか?
文字列で保存されてないですか?
0001とかで

どちらでもいいように
>Where to_number(ID) in
としていたのですが・・・

編集 削除
りな  2010-01-12 15:06:25  No: 36660  IP: 192.*.*.*

IDが文字型の場合と数値型の場合と、両方のテーブルを作成して試したのですが、上手くいきませんでした。WHEREの中にSELECT文を入れると、読みとらなくなります。

編集 削除
HOta  2010-01-12 15:39:39  No: 36661  IP: 192.*.*.*

IDが、選択するランダムの中に該当する値が無いのでしょう。
IDが何なのか。どんな値が入っているかがわからないと、解決できません。

編集 削除
りな  2010-01-12 22:56:57  No: 36662  IP: 192.*.*.*

たびたび不十分な説明、申し訳ございません。

IDを文字型で作成したテーブルでも、IDを数値型で作成したテーブルでも、IDは1001,1002,1003,1004という値を格納しています。現段階では、まだ0001,0002といった文字型でしか表記できないIDをもったデータは登録していません。

編集 削除
りな  2010-01-12 23:17:32  No: 36663  IP: 192.*.*.*

もう一点確認させていただきたいことがあります。以前HOtaさんから教えていただきましたselect文で抽出後、DBGridに表示されたものに対して、update処理しエーブルの1つの項目の値を更新する方法を試したのですが、datasetを認識しないというコンパイルエラーが出てしまします(ちなみに、それぞれのコンポーネントの名前は、DBGrid2,datasource2,query2です)。

やり方として、
・まずselect文にsample関数を組み込んで、データを抽出(ここは、問題なく処理が実行できてます)。

そして、update文に以下のようなwhere文を加えて、updateする。
SQL.Add('Where ID = ' + Dataset.FieldByName('ID').asString);

また、この方法ですと、sqlはselect文からupdate文へ書き換えるときに、一度closeとclearしないといけないので、sqlがリセットされて抽出したものが下に戻ってしまうと思うのですが、これはクエリを2つ(select用と、update用)使用すれば解決するのでしょうか?

様々なアドバイス・情報、本当にありがとうございます。

編集 削除
HOta  2010-01-13 04:31:55  No: 36664  IP: 192.*.*.*

まず1点
GTRさんも書いておられますが、数値型・文字列型のどちらでしょうか?
テーブルの設計書を見ればすぐに判ると思います。
そして、SQLPlusで
Select Trunc(dbms_random.value*1000) as rand
 from TABLE1
 where rownum <= 30
を実行した結果は、Table1に登録しているレコードのIDに必ず有りますか?
ここに無ければ30件の選択結果はありません。0件にもなるでしょう。
必ず30件必要ならば、存在するIDを30件用意しないといけません。

更新の件ですが、この場合、
DBGrid2のDatasorceがdatasource2で、datasource2のDatasetがquery2になります。
with DBGrid2 do
  for I := 0 to SelectedRows.Count - 1 do        //SelectedRowsはDBGrid2のプロパティ
  begin
    Datasorce.Dataset.GotoBookmark(pointer(SelectedRows.Items[I]));  //ちょっと違っていました。
    //DBGrid2のDatasetに対するFlgを立てる処理
    //DBGrid2.SelectedRows.CurrentRowSelected := false;              //選択状態を外す
  end;

//DBGrid2のDatasorceがdatasource2で、datasource2のDatasetがquery2の場合、
//query2は開いているとする。この場合は、別のTQueryを使っています。この方が判りやすいです。
//ただ、query2は再度
with Query1 do
beign
  Close;
  whth SQL do
  begin
    Clear;
    Add('Update Table1');
    Add('Set FLAG = ''1''');
    Add('Where ID = ' + Datasorce.Dataset.FieldByName('ID').AsString);
    //この場合は、SQL文にするためにAsStringを使っています。
    //SQL文はテキストですから。
    //Parameterにすると、Oracle側のフィールドの型に対応しなければいけません。
  end;
  ExecSQL;
end;

また、テーブルを編集する別の方法は、TUpdateSQLを使用します。
TUpdateSQLをフォームに置きます。これをUpdateSQL1とします。
Query2のUpdateObjectプロパティをUpdateSQL1にします。
多分UpdateObjectプロパティをクリックすると、UpdateSQL1が表示されると思います。
UpdateSQL1をダブルクリックすると、編集内容が表示されますので、
オプションのテーブル名を選択して、テーブル項目を押すと項目を表示します。
キーでIDを選び、更新項目でFLGを選びます。
SQL文を生成で、生成すると、SQL文のUpdate,Insert,Deleteがそれぞれできあがります。
この部分は、UpdateSQL1のDeleteSQL,InsertSQL,ModifySQLでそれぞれ編集することも出来ます。
これでQuery2とUpdateSQLが連結されましたので、
with DBGrid2 do
begin
  for I := 0 to SelectedRows.Count - 1 do        //SelectedRowsはDBGrid2のプロパティ
  begin
    Datasorce.Dataset.GotoBookmark(pointer(SelectedRows.Items[I]));
    Datasorce.Dataset.FieldByName('FLAG').AsString := '1';
  end;
  if Datasorce.Dataset.UpdatesPending then
  begin
    try
      Datasorce.Dataset.ApplyUpdates;
    except
      showmessage('更新できません。');
    end;
  end;
end;

編集 削除
HOta  2010-01-13 04:39:42  No: 36665  IP: 192.*.*.*

ちょっと説明が足りな買ったようですが、
Query2のCashedUpdateプロパティをTrueにしてください。
UpdatesPending プロパティは,キャッシュアップデートバッファに未適用のレコードが入っているかどうかを調べます。
未適用のキャッシュアップデートバッファが有る場合は、ApplyUpdatesで適用します。

編集 削除
GTR  2010-01-13 10:43:54  No: 36666  IP: 192.*.*.*

>IDは1001,1002,1003,1004
と言う事は
>Select Trunc(dbms_random.value*1000) as rand from TABLE1
>where rownum <= 30
を実行してもrandの値は0〜999の範囲しか取得しませんので
更新対象データは0件でしょう。
単純にですが
Select Trunc(dbms_random.value*1000)+1001 as rand from TABLE1
等として1001以降の値を取るようにしてみてはどうでしょう?
1001〜1999迄の範囲で取得しますよ。

編集 削除
りな  2010-01-13 11:06:58  No: 36667  IP: 192.*.*.*

HOtaさん、詳しい情報ありがとうございます。

テーブルのIDは文字型です。また、SELECT TRUNC文の結果は、まったくかんけいない数字がROWNUMで指定した数だけ表示されます(ROWNUM<=3でしたら、8,7,7など)。

また、Hotaさんに教えていただいたことを試したのですが、普通のクエリでupdateする処理については、実行時にSQLコマンドエラーになってしまいます。WHERE文のところ
がうまく機能していないのではないかとおもいます。

UPDATESQLについては、私はテーブルTTABLEをつかわず、クエリのみで処理していたのですが、新しくTTABLEを設置し、UPDATESQLを使おうとしたのですが、オプションのところでテーブル名が選択できず、またDATASOURCEからもクエリのみしか指定できませんでした。無理矢理プログラムを書いてみたのですが、認識されずコンパイルエラーになってしまいました。何度も申し訳ないのですが、アドバイスいただけないでしょうか?

編集 削除
りな  2010-01-13 11:10:13  No: 36668  IP: 192.*.*.*

GTRさんへ、

30件のデータ用意して、VALUE*30で実行しても、更新件数が0行になってしまいました。

編集 削除
GTR  2010-01-13 11:17:09  No: 36669  IP: 192.*.*.*

用意したデータの値を教えてもらえませんか?
それじゃないと、更新件数が0になる原因つかめないけど。
>VALUE*30
は0から30になるけどその値が更新対象データに存在するのかな?

編集 削除
HOta  2010-01-13 11:39:47  No: 36670  IP: 192.*.*.*

GTRさんが書かれているとおりでしょう。これでは選択は出来ていませんね。

Update時のコマンドエラーは、どんなエラーが出ていますか?
SQLPlusで同じSQL文を実行して更新できますか?
Oracle側のエラーかDelphi側のエラーかを切り分けてください。
また、Delphi側でのエラーなら、適当なソースを提示してください。

TUpdateSQLは、前に書いているように、TQueryのUpdateObjectプロパティに設定します。
TTableではありません。

編集 削除
何が分からないのか分からない  2010-01-13 14:42:34  No: 36671  IP: 192.*.*.*

だらだらやっていますが、私はりなさんがSQLの文法覚えるのが先だと思いますよ。
全部教えてもらうより、SQLの文法覚えるよう努力しましょうよ。
括弧が足りないなら追加すればいいのです。

SQLエラーが出るならエラー番号を毎回提示してください。
「主キー」が分からないようであれば調べてみてください。

厳しい言い方ですが、ご自身でSQLPlusで実行して試してみてから
質問されてはいかがでしょうか?
SQLのエラーについては回答できると思いますが、ロジック(どうしてそうなる?)が
分からないのであればいくら質問してもこの先やっていけませんよ。

編集 削除
SQL初心者  2010-01-13 21:55:43  No: 36672  IP: 192.*.*.*

SQLの参考書はお持ちでしょうか?
当方独学にて学習中ですが、
小さな練習プログラムを沢山作り、こつこつ覚えてみてはどうでしょうか?

その方が案外近道かも知れないですよ?

いかに優れたアドバイスを貰ったとしても、
自分の技量を超えたものを作るのは簡単ではありません。

エラーが出た際にそれが露呈したなら、自力での対応が出来ないものです。

何かの本に書いてあったのですが、
こつこつ沢山のソフトを作り、
学習していくのが最善の道だそうです。

当方も小さなサンプルプログラムを組みながら、学習中です。
一つの事が出来るようになったら次に進み、
そこでどうしてもわからなかったら質問する・・・

でないと、他力本願ですし、
自分で組んだプログラムなのに、エラーが発生した際に(処理の意味が理解できず)自分で修正する事が困難になります。

いきなり一回で思い通りのプログラムを作るのは簡単ではありません。
小さな経験を積み重ね、それがつもり積もって、大きな結果を残せるものだと思います。


当方も未熟者で失礼申し訳なく思いますが、
目的のものを作れる技量を身につけられるよう、
頑張ってみてはどうでしょうか?

目的のソフトを自力でも組み立てられるよう、応援しています。
何事も急がば回れかも知れないですよ。

編集 削除
りな  2010-01-14 13:19:13  No: 36673  IP: 192.*.*.*

皆様のご指摘ごもっともだと思います。甘えていました。

最後にもうひとつだけ質問させてください。

rownum <= 50ついて、何度か試したのですが、常に50件抽出されるというわけではないみたいです。 rownum = 50とすると なにも抽出されなくなります(45や48、39件のデータが抽出されることがあります)。50件なら50件と指定した数字がそのまま抽出させる書きかたはないのでしょうか?

SQLPLUSでいろいろとテーブル全体のデータ件を変えても、常に指定した件数が抽出されませんでした。
ですので、テーブル全体の件数に関係はないみたいです。

編集 削除
TS  2010-01-14 14:12:57  No: 36674  IP: 192.*.*.*

rownumの意味がおわかりでしょうか。
私は知りませんので下記参考
http://www.shift-the-oracle.com/sql/column/rownum.html

where rownum <= 50  データ行ナンバー50行までの行が対象
where rownum = 50  データ行ナンバー50行だけを対象にする。

編集 削除
TS  2010-01-14 14:17:17  No: 36675  IP: 192.*.*.*

本当に知りません、ので上記の説明は不正です。ご免なさい。
参考ホームページに説明があります。

編集 削除
HOta  2010-01-14 16:04:05  No: 36676  IP: 192.*.*.*

常に指定件数抽出するのなら、別に抽出するIDをOracleで作るのではなく、Delphi側で作ればどうですか?登録しているIDを一度TStringListなどに読み込んでおいて、Randomで作ったIDがその中に存在するかを確かめて指定件数分抽出するIDを作れば必ず指定件数分、抽出出来るのではないでしょうか?Oracleのdbms_randomでIDを作っても必ず存在するIDではないのでしょう。

編集 削除
りな  2010-01-17 16:03:52  No: 36677  IP: 192.*.*.*

HOtaさんに教えていただいたやり方をいろいろと考えてみたのですが、私の知識不足のため、やり方がわかりませんですた。申し訳ございません。
ランダム行にフラグを立てるところまでは、うまくできるようになりました。しかし、取得件数(行数の)制御がいかない状態です(以下のコード)。

sql.add('update table set flag = ''1'' where id');
sql.add('in (select id from (select dbm_random.random() as rand');
sql.add(', id from table order by rand) where rownom <= 20);


今後は、行数制御のためlimit関数などを使って、試したいと思います。

ありがとうございました。

編集 削除
HOta  2010-01-17 19:25:17  No: 36678  IP: 192.*.*.*

例えば、DelphiのRandam関数でIDを得て、実際のテーブルに有るかをSQL文で調べ無ければさらにIDを得ることを、必要な数作ればいかがですか?
作ったIDをTStringListなどに貯めておいて、
SQL.Add('Where ID in (' + StringList1.commmatext + ''')''' );
などとすれば必ず必要な数のレコードが得られます。

編集 削除
りな  2010-01-20 00:10:59  No: 36679  IP: 192.*.*.*

HOtaさん:たくさんのアドバイスありがとうございます。
HOtaさんに教えていただいたやり方に挑戦してみたのですが、TStringlistがうまく使いこなせませんでした。

ただ、別の方法で指定の件数をランダムに取得・フラグをたて行進することができました。方法として、前回書いた命令文をrepeat文で囲って、指定件数を取得するまで、処理をするというものです。

何度も申し訳ないのですが、最後の最後にお伺いしたいことがあります。
もし項目のIDにプライマリキーがかかってなく、重複がゆるされている状態がある場合、同じIDのが複数存在する可能性があります。その複数存在する同じIDをまとめて1つのデータとして、同じようにランダム行取得・更新処理をしようと、distinctを以下の文に付け加えたら、データが取得できなくなりました。重複データを1件のデータとして取得し、フラグを立てるには方法をご存知でしょうか?

sql.add('update table set flag = ''1'' where id');
sql.add('in (select distinct ID from (select dbm_random.random() as rand');
sql.add(', ID from table order by rand) where rownom <= 20);

編集 削除
めぞ  2010-01-20 00:41:23  No: 36680  IP: 192.*.*.*

当初の質問が解決したらこのスレは一度閉じてみては
どうでしょう。
#意地悪で書いてるわけではありません。

ココまでの質問で何が解決したのか
何が分からないのかを再度質問してみてください。
意外と頭の中が整理できると思いますよ。

http://www.hyuki.com/writing/techask.html
一読お勧めします。

編集 削除
めぞ  2010-01-20 01:01:11  No: 36681  IP: 192.*.*.*

最初の方は私もなんだかなーと思いますけど
>実行手順 —— 手順は箇条書きで書きましょう
あたりからは読んだ方がいいかと。

編集 削除
HOta  2010-01-20 03:56:56  No: 36682  IP: 192.*.*.*

判らなくなった場合は、条件を分けて考えましょう。
この場合、条件文がおかしくなっていませんか?
Select distinct ID 以下の文は実行して選択できていますか?
これができていれば複数の更新も可能のはずです。
SQL文は、それぞれのSQL句を組み合わせて使いますので、
SQL句の意味をよく理解して使いましょう。

編集 削除
りな  2010-01-21 07:23:02  No: 36683  IP: 192.*.*.*

お伺いしていたメイン問題が解決しました。みなさん、本当にありがとうございました。

編集 削除