- PR -

容量計算のSQLロジックの質問(数学)

投稿者投稿内容
yangjiayi
ベテラン
会議室デビュー日: 2007/10/04
投稿数: 59
投稿日時: 2009-04-03 19:42
お世話になります。

現在、SQL文で、容量計算のロジックを設計しています。
求めているのがグラム(g)の結果です。

仮に、以下のバターンがあります。

「1.8kg」
「250g+50g」
「70g×2」
「(150g×2)×2」
「(130g+20g)×3」
「100g(1人前)×4」

以上の内容を一旦半角や演算式に変換する。(ここまで実現できます)
「1.8kg」
「250g+50g」
「70g*2」
「(150g*2)*2」
「(130g+20g)*3」
「100g(1人前)*4」

ここまではOKですが、結果は以下のように求めたいです。
「1800」
「300」
「140」
「600」
「300」
「400」

計算ロジックはどうやって実現できるのでしょうか。
私はなかなかいい方法を思いつかないです。

ぜひ知恵を貸してください。
_________________
yangjiayi
platini
大ベテラン
会議室デビュー日: 2002/12/03
投稿数: 193
投稿日時: 2009-04-03 21:58
質問1:SQLだけで実現しなくてはいけないのですか?
    正直、SQLでこれをやることは、かなり無謀だと思いますが。
質問2:おっしゃるところのSQLとはSQL文だけ(例えばSQL92準拠など)を
    意味していますか。それとも例えばSQLServerであれば Transact-SQLやSQLCLR、
    Oracleであれば PL/SQLのようなサーバサイドプロシージャも「SQL」ですか。
質問3:実現希望例の中でも、
引用:

100g(1人前)×4


はせめて、無視(エラー扱い)にできないのでしょうか。


引用:

計算ロジックはどうやって実現できるのでしょうか。
私はなかなかいい方法を思いつかないです。



皮肉・・・ではなくて、
私はなかなかいい方法を思いつかないです。
が当然で、この計算ロジックをパッと思いつく人は
多分、掲示板に質問せずに、自力で解決する力量の人だと思います。



引用:

100g(1人前)×4


をエラー扱いにさえできれば、かなり複雑になるとはいえ、
正規表現なんかを使って、相当頑張れば何らかのロジックで処理できそうですが、
それでも【SQL】の世界内だけでは無謀なような。。。。
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2009-04-03 23:34
この辺のことをまじめにやろうとすると、
ちょっとした式言語エンジンを実装することになります。
SQLのプロシージャで、ではできなくもないでしょうけど、相当難しいものです。
BNFや四則演算あたりのキーワードで、サンプルを探してみると、
SQLでやる意味がないのが理解できるかと思います。

世の中には適材適所という言葉があります。
ステーキを食べるのにスプーンを、まず使うことはないでしょうし、
スープを飲むのにフォークとナイフを使うこともまずないでしょう。
それと同じことです。

SQLはデータを集計したり取得する言語で、
SQLを呼び出す側は、データを加工して入出力するためにあります。
yangjiayi
ベテラン
会議室デビュー日: 2007/10/04
投稿数: 59
投稿日時: 2009-04-08 01:17
お世話になります。
自分で、作りました。
かつのりさんとplatiniさんのコメントありがとうございました。

コード:
50g(10g*5袋)+0.064kg(10g*5袋)+13袋(350g)



こういう複雑の計算式でも対応できていると思います。

まず、kを*1000に入れ替えます。
それから、数字、小数点、+、*、()、以外の文字列を削除します。

コード:
50g(5g*10)+0.050*1000g(5g*10)+13(350g)



それから、このコード:
コード:
declare @strx varchar(max)
declare @stry varchar(max)
declare @flg1 varchar(1)
declare @flg2 varchar(1)
declare @flga varchar(1)
declare @cnt int

set @strx = '50g(5g*10)+0.050*1000g(5g*10)+13(350g)'

--値セット文字列
set @stry = ''

--開け括弧フラグ
set @flg1 = '0'
--閉じ括弧フラグ
set @flg2 = '0'
--g文字フラグ
set @flga = '0'

--初期化
set @cnt = 1

--loop for right to left
while @cnt <= LEN(@strx)
	begin
		--string g
		if SUBSTRING(@strx,@cnt,1) = 'g'
			begin
				set @flga = '1'
				if @flga = '1' and @flg1 = '0'
					begin
						set @stry = @stry + SUBSTRING(@strx,@cnt,1)
					end
				--nextstr is (?
				if SUBSTRING(@strx,@cnt + 1 ,1) = '(' and @flg1 = '0' and @flg2 = '0' and @flga = '1'
					begin
						set @flg1 = '1'
					end
			end
		
		--string (
		if SUBSTRING(@strx,@cnt,1) = '('
			begin
				if @flga = '1' and @flg1 = '1' and @flg2 = '0'
					begin
						set @cnt += 1
						continue
					end 
				else
					begin
						set @stry += SUBSTRING(@strx,@cnt,1)
					end
			end
			
		--string )
		if SUBSTRING(@strx,@cnt,1) = ')'
			begin
				if @flg1 = '1' and @flga = '1'
					begin
						set @flg1 = '0'
						set @flga = '0'
						set @cnt += 1
						continue
					end
				if @flg1 = '0' and @flga = '1'
					begin
						set @stry += SUBSTRING(@strx,@cnt,1)
					end
			end
		
		--string 0-9
		if SUBSTRING(@strx,@cnt,1) between '0' and '9'
			begin
				if @flg1 = '0' and @flga = '0'
					begin
						set @stry += SUBSTRING(@strx,@cnt,1)
					end
				if @flg1 = '0' and @flga = '1'
					begin
						set @stry += SUBSTRING(@strx,@cnt,1)
					end
			end
			
		--string +
		if SUBSTRING(@strx,@cnt,1) = '+'
			begin
				if @flg1 = '0' and @flga = '0'
					begin
						set @stry += SUBSTRING(@strx,@cnt,1)
					end
				if @flg1 = '0' and @flga = '1'
					begin
						set @stry += SUBSTRING(@strx,@cnt,1)
					end
			end
			
		--string *
		if SUBSTRING(@strx,@cnt,1) = '*'
			begin
				if @flg1 = '0' and @flga = '0'
					begin
						set @stry += SUBSTRING(@strx,@cnt,1)
					end
				if @flg1 = '0' and @flga = '1'
					begin
						set @stry += SUBSTRING(@strx,@cnt,1)
					end
			end
			
		--string .
		if SUBSTRING(@strx,@cnt,1) = '.'
			begin
				if @flg1 = '0' and @flga = '0'
					begin
						set @stry += SUBSTRING(@strx,@cnt,1)
					end
				if @flg1 = '0' and @flga = '1'
					begin
						set @stry += SUBSTRING(@strx,@cnt,1)
					end
			end
		
		--プラスカウント				
		set @cnt += 1		
	end
	
--loop for left to right

--開け括弧フラグ
set @flg1 = '0'
--閉じ括弧フラグ
set @flg2 = '0'
--g文字フラグ
set @flga = '0'

--loop right to left 結果セット
set @strx = @stry 

--初期化
set @cnt = LEN(@strx)
set @stry = ''

while 1 = 1
	begin
		--string )
		if SUBSTRING(@strx,@cnt,1) = ')'
			begin
				set @flg2 = '1'
				if @flg2 = '1' and @flg1 = '0' and (@flga = '0' or @flga = '1')
					begin
						set @stry = SUBSTRING(@strx,@cnt,1) + @stry 
					end
			end
							
		--string (
		if SUBSTRING(@strx,@cnt,1) = '('
			begin
				if @flg2 = '1' and @flga = '1'
					begin
						set @stry = SUBSTRING(@strx,@cnt,1) + @stry 
					end
				--nextstr is 0-9?
				if SUBSTRING(@strx,@cnt - 1 ,1) between '0' and '9' and @flg1 = '0' and @flg2 = '1' and @flga = '1'
					begin
						set @flg1 = '1'
					end
			end
			
		--string g
		if SUBSTRING(@strx,@cnt,1) = 'g'
			begin
				set @flga = '1'
				set @stry = SUBSTRING(@strx,@cnt,1) + @stry 

			end	
			
		--string +
		if SUBSTRING(@strx,@cnt,1) = '+'
			begin
				set @stry = SUBSTRING(@strx,@cnt,1) + @stry 
			end
			
		--string *
		if SUBSTRING(@strx,@cnt,1) = '*'
			begin
				set @stry = SUBSTRING(@strx,@cnt,1) + @stry 
			end
			
		--string .
		if SUBSTRING(@strx,@cnt,1) = '.'
			begin
				set @stry = SUBSTRING(@strx,@cnt,1) + @stry 
			end	
	
		--string 0-9
		if SUBSTRING(@strx,@cnt,1) between '0' and '9'
			begin
				if @cnt > 1 and @flg1 = '1' and @flg2 = '1' and @flga = '1'
					begin
						if SUBSTRING(@strx,@cnt - 1,1) not between '0' and '9'
							begin
								set @flg1 = '0'
								set @flg2 = '0'
								set @flga = '0'
								set @cnt -= 1
								continue
							end
						else
							begin
								set @cnt -= 1
								continue
							end	
					end
				if @flg1 = '0' and (@flg2 = '0' or @flg2 = '1') and (@flga = '0' or @flga = '1')
				set @stry = SUBSTRING(@strx,@cnt,1) + @stry
			end
			

		--マイナスカウント
		set @cnt -= 1
		
		if @cnt = 0
			begin
				break
			end
	end
	
select @stry as 出力



参考まで。

結果:

出力
50g+0.050*1000g+(350g)
yangjiayi
ベテラン
会議室デビュー日: 2007/10/04
投稿数: 59
投稿日時: 2009-04-08 01:20
その後、

replace('50g+0.050*1000g+(350g)','g','')

Replace後、

Declare @SQLString nvarchar(4000)
Declare @Val float
Set @SQLString = 'Select @Val = 50+0.050*1000+(350)'
Exec sp_executesql @SQLString, N'@Val float Output',@Val Output
Select @Val

結果:
450
_________________
yangjiayi
yangjiayi
ベテラン
会議室デビュー日: 2007/10/04
投稿数: 59
投稿日時: 2009-04-08 01:22
もう深夜1:30で、寝ます。
お疲れ様でした。^^
_________________
yangjiayi
デューン
大ベテラン
会議室デビュー日: 2004/04/21
投稿数: 174
お住まい・勤務地: Tokyo
投稿日時: 2009-04-08 10:28
重箱の隅をつつくようで申し訳ないですが、

引用:

50g(10g*5袋)+0.064kg(10g*5袋)+13袋(350g)


=50+64+350
=464

引用:

50g(5g*10)+0.050*1000g(5g*10)+13(350g)


=50+50+350
=450g
変わってます。

# 演算子に続かない括弧は無視するのかと思いきや、そう単純でもないみたいですね
# gに続く括弧は括弧を無視、数値に続く括弧は括弧を採用という感じでしょうか

もちろんtypoを指摘したいのではなくて、フリーテキストって何かかれるかわからないので、入力の規約がない限り、完全なものは難しいでしょう。

今回指摘したように人間の目で見て括弧内と括弧の外と異なる場合にどちらが正かは判断できないでしょうし、

5g×13袋(65g)

13袋×5g(65g)

演算結果が変わりませんか?

# 否定的ですいませんが、リリースして「あら?っ」てなるよりはマシかと思いましたので


platini
大ベテラン
会議室デビュー日: 2002/12/03
投稿数: 193
投稿日時: 2009-04-08 12:26
同じく重箱の隅つつきですが、
フリーテキストで入力させるなら、

よく問題になる外字のkg(1文字相当でkgと表現)が入力されたり、
全角KGや全角Kgといったケースへの対処も発生すると思います。

仕様前提条件で、それはありえない!と排除できるなら余計なおせっかいですが。

ユーザーがキーボードパンチで入力することがデータソースとなるなら、
外字のkgは、IMEの変換第一候補になってたりします(苦笑)

================================================
この開発目的が自社用システムなのか、請負開発システムなのかで
考え方が相当違ってくるように思います。

請負開発だとすると、リリース後も、無茶苦茶な(想定外の)入力文字列に対しても

なぜこれが計算できない!瑕疵だ!
と言われて、泥沼無限地獄にはまり込むような気がします。



[ メッセージ編集済み 編集者: platini 編集日時 2009-04-08 13:16 ]

スキルアップ/キャリアアップ(JOB@IT)