我正在研究Foxpro,以创建一个简单的应用程序来处理两个表A
和B
(tableB的大小>>
的大小)的数据。来自Excel spreadsheet
的数据将导入到这两个表中。
tableA
id balance load state
1 10 null l
2 22 null l
3 31 null l
tableB
Load id id ord fact type 1st value rounded value state
1 1 1 0.09 1 null null l
2 1 2 0.02 0 null null l
3 1 3 0.13 1 null null l
4 1 4 -0.05 0 null null l
5 2 1 0.01 1 null null l
6 2 2 0.092 1 null null l
7 2 3 0.03 0 null null l
8 3 1 0.14 1 null null l
9 3 2 0.12 0 null null l
10 3 3 -0.02 0 null null l
我的朋友要我编写Foxpro代码来执行以下操作:首先,创建包含上述列的空tableA和tableB。每天将通过excel电子表格中的(数十万)数据加载每一列。其次,对于每个唯一ID ,代码使用给定的公式更新1st value
,rounded value
和load
的3列:
1st value[i] = If(Type[i]=0, load[i-1]*fact[i], load[i-1]*fact[i]/(1-fact[i]))
1st value[1] = If(Type[1]=0, balance[1]*fact[1], balance[1]*fact[1]/(1-fact[1]))
rounded value[i] = If(1st value[i]>0, rounddown(1st value[i], 1), roundup(1st value[i],2)
load[i+1] = load[i] + rounded value[i+1] (i >= 1)
load[1] = balance[1] + rounded value[1]
我想我必须创建一个类似于以下的表来存储以上用于该步骤的计算:
Calculation Table
balance id ord 1st value rounded value load
10 1 1 0.989 0.90 10.9 (= 10 + 0.9)
10.9 1 2 0.218 0.20 11.1 (= 10.9 + 0.2)
11.1 1 3 1.658 1.60 12.7 (= 11.1 + 1.6)
11.06 1 4 -0.635 -0.64 11.06 (=12.7 + (-0.64))
所需的输出
使用Calculation Table
中的结果,我们对原始tableA
和tableB
进行如下更新:
tableB
Load id id ord 1st value rounded value state
1 1 1 0.989 0.90 calculated
2 1 2 0.218 0.20 calculated
3 1 3 1.658 1.60 calculated
4 1 4 -0.635 -0.64 calculated
5 2 1 ... .... calculated
6 2 2 ... .... calculated
tableA (Note: for each value in `load id`, the `load` column only stores the **last** value in the `calculation` table which corresponds to maximum `ord`)
id balance load state
1 10 9.5 calculated
2 22 ... calculated
3 31 ... calculated
任何人都可以在创建tableB
,计算列1st value
,rounded value
和load
的结果并将其存储到calculation
表的语法中帮我吗在tableA和tableB之间的Inner Join
列上使用id
函数,并更新tableB
?
我的尝试
第一步(使用上面显示的列字段创建两个表A和B)
CREATE TABLE tableA;
( id int, ;
balance double, ;
load C(240), ;
state C(240), ;)
CREATE TABLE tableB;
( Load id int, ;
id int, ;
ord int, ;
fact double, ;
type binary (not sure....) ;
1st value C(240),;
rounded value C(240), ;
state C(240), ;)
答案 0 :(得分:1)
添加为另一个答案以防止混乱。如果需要,我可以做进一步的解释。在这里,我使用了与样本数据匹配的Excel范围。您可以将范围替换为实际范围(以及excel文件名):
GetDataFromExcel("c:\myFolder\myExcel.xlsx", "B9:E12", "G9:N19")
DoCalculation()
Select crsA
Browse
Select crsB
Browse
Procedure DoCalculation
*1st value[1] = If(Type[1]=0, balance[1]*fact[1], balance[1]*fact[1]/(1-fact[1]))
*rounded value[i] = If(1st value[i]>0, rounddown(1st value[i], 1), roundup(1st value[i],2)
*rounded value[1] = If(1st value[1]>0, rounddown(1st value[1], 1), roundup(1st value[1],2)
*load[1] = balance[1] + rounded value[1]
* i > 1 - ord > 1
*1st value[i] = If(Type[i]=0, load[i-1]*fact[i], load[i-1]*fact[i]/(1-fact[i]))
*rounded value[i] = If(1st value[i]>0, rounddown(1st value[i], 1), roundup(1st value[i],2)
*load[i+1] = load[i] + rounded value[i+1] (i >= 1)
Local lnBalance
Select CrsB
Index On Padl(Id,10,'0')+Padl(ord,10,'0') Tag ALinkB
Select crsA
Set Relation To Padl(Id,10,'0') Into CrsB
Scan
lnBalance = crsA.Balance
Select CrsB
Scan While Id = crsA.Id
Replace ;
firstValue With m.lnBalance*fact / Iif(!Type, 1, 1-fact), ;
roundVal With Iif(firstValue > 0, ;
roundDown(firstValue,1), ;
roundUp(firstValue, 2))
lnBalance = m.lnBalance + CrsB.roundVal
Endscan
Select crsA
Replace Load With m.lnBalance
Endscan
Endproc
Procedure GetDataFromExcel(tcExcelFileName, tcTableARange, tcTableBRange)
Local lcConStr
lcConStr = ;
'Provider=Microsoft.ACE.OLEDB.12.0;'+;
'Data Source='+Fullpath(m.tcExcelFileName)+';'+;
'Extended Properties="Excel 12.0;HDR=Yes"'
Local lcSQLA, lcSQLB
TEXT to lcSQLA textmerge noshow
Select [id], [balance], [load], [state]
from [Sheet1$<< m.tcTableARange >>]
ENDTEXT
TEXT to m.lcSQLB textmerge noshow
select
[Load Id] as LoadId,
[Id], [Ord], [Fact], [Type],
[1st value] as firstValue,
[Rounded value] as roundVal,
[State]
from [Sheet1$<< m.tcTableBRange >>]
ENDTEXT
ADOQuery(m.lcConStr, m.lcSQLA, "crsTableA")
ADOQuery(m.lcConStr, m.lcSQLB, "crsTableB")
Select Cast(Id As Int) As Id, Cast(Balance As Double) As Balance, ;
Cast(Load As Double) As Load, Cast(State As c(1)) As State ;
from crsTableA ;
into Cursor crsA ;
readwrite
Select Cast(LoadId As Int) As LoadId, ;
Cast(Id As Int) As Id, Cast(ord As Int) As ord, ;
Cast(fact As Double) As fact, Cast(Type As logical) As Type, ;
Cast(firstValue As Double) As firstValue, ;
Cast(roundVal As Double) As roundVal, ;
Cast(State As c(1)) As State From crsTableB ;
into Cursor CrsB ;
readwrite
Use In (Select('crsTableA'))
Use In (Select('crsTableB'))
Endproc
Procedure roundUp(tnValue, tnPlaces)
If Round(m.tnValue, m.tnPlaces) = m.tnValue
Return m.tnValue
Else
Return Round(m.tnValue+((10^-(m.tnPlaces+1))*5), m.tnPlaces)
Endif
Endproc
Procedure roundDown(tnValue, tnPlaces)
If Round(m.tnValue, m.tnPlaces) = m.tnValue
Return m.tnValue
Else
Return Round(m.tnValue-((10^-(m.tnPlaces+1))*5), m.tnPlaces)
Endif
Endproc
Procedure ADOQuery(tcConStr,tcQuery,tcCursorName)
Local oConn As 'ADODB.Connection'
Local oRS As ADODB.RecordSet
oConn = Createobject('ADODB.Connection')
oConn.Mode= 1 && adModeRead
oConn.Open( m.tcConStr )
oRS = oConn.Execute(m.tcQuery)
RS2Cursor(oRS,m.tcCursorName)
oRS.Close
oConn.Close
Endproc
Procedure RS2Cursor(toRS, tcCursorName) && simple single cursor - not intended for complex ones
tcCursorName = Iif(Empty(m.tcCursorName),'ADORs',m.tcCursorName)
Local xDOM As 'MSXML.DOMDocument'
xDOM = Createobject('MSXML.DOMDocument')
toRS.Save(xDOM, 1)
Xmltocursor(xDOM.XML, m.tcCursorName)
Endproc
编辑:我编辑了下面答案的另一个答案。现在您的问题:
- 不要在“过程过程”之后调用GetDataFromExcel(“ c:\ myFolder \ myExcel.xlsx”,“ B9:E12”,“ G9:N19”) GetDataFromExcel(tcExcelFileName,tcTableARange,tcTableBRange)??
不。过程始终放在prg文件中的常规执行代码之后。 IOW,如果您的PRG具有:
Do Something
* ...
Procedure SomeProcedure
* ...
endproc
Procedure Something
endproc
代码从调用Something开始,然后执行代码,直到看到第一个Procedure调用(或FUNCTION,DEFINE CLASS)。可能是一个过程(如示例中所述)或单独的程序。
- 不应在roundDown(firstValue,1),之前调用Procedure roundUp和Procedure roundDown; roundUp(firstValue,2))??
否,与上述相同。您所说的更多内容看起来像核心C的规则。
- 此行中的左ID是否在ID = crsA.Id时扫描来自CrsB?另外,为什么从crsA变为CrsA?这是一个 错字? – user177196 5分钟前
是的。它来自crsB。但从某种意义上说,您是对的,我应该明确,并在其中添加别名:
Scan while crsB.Id = crsA.Id
在VFP中,如果您不包含别名,则假定为当前别名。
我们正在外部循环中扫描crsA。然后,我们切换回crsB,然后再切换到crsA(实际上,扫描命令会记住它所关联的别名,并且在隐式命中endscan时会进行此切换,但我希望是明确的)。
编辑:
Select CrsB
Index On Padl(Id,10,'0')+Padl(ord,10,'0') Tag ALinkB
Select crsA
Set Relation To Padl(Id,10,'0') Into CrsB
在前两行,我们选择crsB游标并在其上创建索引。索引表达式包含ID和Old字段。 VFP在索引键中不支持多个列名,但是它支持表达式。用10个零填充两个字段,我们创建的键如下:
Id,Ord:以2,3为例,其键为00000000020000000003
我们可以使它更小,但是由于不知道Id有多大,所以Ord可以使其长度为10以适合任何32位整数值。
然后在第3、4行上,我们选择光标crsA,然后通过表达式Padl(Id,10,'0')-用10个零填充的ID将crsA的关系设置为crsB。从crsA Id:1开始,其关联键为0000000001(匹配所有以0000000001开头的索引键,无论Ord部分是什么-顺带一提的Ord都必须确保它们由Ord排序)。
实际上,当记录指针指向crsA中的Id:1时,在crsB中将自动匹配那些具有Id:1的对象(最好的浏览方式-浏览crsB,然后选择crsA并浏览。在crsA中导航时,您会会看到crsB的浏览窗口将仅显示具有匹配ID的行)。从概念上讲,这看起来像在两个游标中控制记录指针一样:
crsA (id) crsB (Id, Ord)
1 ----+------- 1,1
+------- 1,2
+------- 1,3
+------- 1,4
2 ----+------- 2,1
+------- 2,2
+------- 2,3
我之所以使用它,是因为它是VFP的强大功能,是表达您想要的内容的一种简便方法。也可以通过使用SQL Update来实现相同的功能,但是,VFP的SQL并不那么强大,并且编写起来会更加复杂(对于[1]来说很容易,但对于> 1情况却变得很复杂-在[1]中也不是那么容易其他后端也很遥远,但是随着时间的推移,诸如postgreSQL,MS SQL Server等后端已经获得了对此类查询的更多支持)。
答案 1 :(得分:1)
(添加另一个答案是因为其他人阅读时间太长了)
您可以使用此数据集尝试代码吗 (drive.google.com/open?id=1uCWwt5ubd2_F8w2gsh3v4VDpibWz7PAz)查看是否 您将从代码中获得两个输出表,每个输出表与 我为您上传的上一个Excel工作表中显示的一个?
我下载了该电子表格,这是我需要更改的内容: 表A和B的范围是C8:F35和H8:O62。您的“余额”也称为“基数”。编辑新代码(下载到d:\ temp \ workbook2.xlsx)以匹配范围,将“ balance”平衡为“ base”:
* Get the data from given excel filename and ranges
* first range is tableA, second one is tableB
GetDataFromExcel("d:\temp\WorkBook2.xlsx", "Sheet1$C8:F35", "Sheet1$H8:O62")
* Now data is in cursors csrA and crsB do the calculation in these
DoCalculation()
* Done. Show the results selecting and browsing the crsA and B
Select crsA
Browse
Select crsB
Browse
* Get specific fields only from crsB
Select loadId, id, ord, firstVal, roundedVal, state ;
from crsB ;
into cursor crsBCustom ;
nofilter
browse
* Check data from both cursors (join)
* I chose the fields as I see fit
* ta and tb are local aliases for crsA and crsB
* helping to write shorter SQL in this case
Select tb.LoadId, tb.Id, ta.base, ta.load, ;
tb.firstValue, tb.roundVal, ;
ta.State as StateA, tb.State as StateB ;
from crsA ta ;
inner join crsB tb on ta.Id = tb.Id ;
order by tb.Id, tb.Ord ;
into cursor crsBoth ;
NoFilter
browse
* Does the specific calculations on specific data
Procedure DoCalculation
*1st value[1] = If(Type[1]=0, Base[1]*fact[1], Base[1]*fact[1]/(1-fact[1]))
*rounded value[i] = If(1st value[i]>0, rounddown(1st value[i], 1), roundup(1st value[i],2)
*rounded value[1] = If(1st value[1]>0, rounddown(1st value[1], 1), roundup(1st value[1],2)
*load[1] = Base[1] + rounded value[1]
* i > 1 - ord > 1
*1st value[i] = If(Type[i]=0, load[i-1]*fact[i], load[i-1]*fact[i]/(1-fact[i]))
*rounded value[i] = If(1st value[i]>0, rounddown(1st value[i], 1), roundup(1st value[i],2)
*load[i+1] = load[i] + rounded value[i+1] (i >= 1)
*declare local variable
Local lnBase
* select crsB and create an index there
Select CrsB
Index On Padl(Id,10,'0')+Padl(ord,10,'0') Tag ALinkB
* select crsA as parent and link to crsB
* using the "id" part of index
Select crsA
Set Relation To Padl(Id,10,'0') Into CrsB
* start looping the rows
Scan
* working with a new Id (1, 2, ...)
* save base value to m.lnBase
lnBase = crsA.Base
* select crsB and start looping the rows there
* because of the index in effect and the relation created
* pointer would be on the first crsB row with a matching Id
* and since Ord is also part of the index the first row of
* given Id
* Limit the looping in crsB (child table) to Id in crsA
* using WHILE clause
Select CrsB
Scan While Id = crsA.Id
* do replacing starting on first row of this Id (Ord=1)
* we don't have any scope clauses in replace, thus
* we are doing "single row" updates
Replace ;
firstValue With m.lnBase*fact / Iif(!Type, 1, 1-fact), ;
roundVal With Iif(firstValue > 0, ;
roundDown(firstValue,1), ;
roundUp(firstValue, 2))
* after each replace update m.lnBase value
* to use in next row
lnBase = m.lnBase + CrsB.roundVal
Endscan
* completed updating crsB
* select crsA and also update crsA.base with final 'load' value
Select crsA
Replace Load With m.lnBase
Endscan
* Update state to 'Calculated'
Update crsA set state = 'Calculated'
Update crsB set state = 'Calculated'
Endproc
* Get data from excel with given filename and ranges
* This code is not generic and expects the
* data to be in a specific format.
* Does not do any error check
Procedure GetDataFromExcel(tcExcelFileName, tcTableARange, tcTableBRange)
* declare and define the connection string to excel
Local lcConStr
lcConStr = ;
'Provider=Microsoft.ACE.OLEDB.12.0;'+;
'Data Source='+Fullpath(m.tcExcelFileName)+';'+;
'Extended Properties="Excel 12.0;HDR=Yes"'
* Declare and define the 2 SQL needed to get data for A and B
* rename the fields in SQL for easier handling
Local lcSQLA, lcSQLB
TEXT to lcSQLA textmerge noshow
Select [id], [base], [load], [state]
from [<< m.tcTableARange >>]
ENDTEXT
TEXT to m.lcSQLB textmerge noshow
select
[Load Id] as LoadId,
[Id], [Ord], [Fact], [Type],
[1st value] as firstValue,
[Rounded value] as roundVal,
[State]
from [<< m.tcTableBRange >>]
ENDTEXT
* Execute the queries and place results in given cursors
ADOQuery(m.lcConStr, m.lcSQLA, "crsTableA")
ADOQuery(m.lcConStr, m.lcSQLB, "crsTableB")
* Sanitize the cursors a bit
* (OledB query would assign rather generic datatypes)
Select Cast(Id As Int) As Id, Cast(Base As Double) As Base, ;
Cast(Load As Double) As Load, Cast(State As c(50)) As State ;
from crsTableA ;
into Cursor crsA ;
readwrite
Select Cast(LoadId As Int) As LoadId, ;
Cast(Id As Int) As Id, Cast(ord As Int) As ord, ;
Cast(fact As Double) As fact, Cast(Type As logical) As Type, ;
Cast(firstValue As Double) As firstValue, ;
Cast(roundVal As Double) As roundVal, ;
Cast(State As c(50)) As State From crsTableB ;
into Cursor CrsB ;
readwrite
Use In (Select('crsTableA'))
Use In (Select('crsTableB'))
Endproc
* roundUp and down custom functions
* RoundUp and Down excel style
* Not correct math wise IMHO
Procedure roundUp(tnValue, tnPlaces)
Local lnResult, lnValue
lnValue = Abs(m.tnValue)
If Round(m.lnValue, m.tnPlaces) != m.lnValue
lnValue = Round(m.lnValue+((10^-(m.tnPlaces+1))*5), m.tnPlaces)
Endif
Return Sign(m.tnValue) * m.lnValue
Endproc
Procedure roundDown(tnValue, tnPlaces)
Local lnResult, lnValue
lnValue = Abs(m.tnValue)
If Round(m.lnValue, m.tnPlaces) != m.lnValue
lnValue = Round(m.lnValue-((10^-(m.tnPlaces+1))*5), m.tnPlaces)
Endif
Return Sign(m.tnValue) * m.lnValue
Endproc
* Generic function to query a given data source
* and place results in a cursor
Procedure ADOQuery(tcConStr,tcQuery,tcCursorName)
Local oConn As 'ADODB.Connection'
Local oRS As ADODB.RecordSet
oConn = Createobject('ADODB.Connection')
oConn.Mode= 1 && adModeRead
oConn.Open( m.tcConStr )
oRS = oConn.Execute(m.tcQuery)
RS2Cursor(oRS,m.tcCursorName)
oRS.Close
oConn.Close
Endproc
* Helper function to ADOQuery to convert
* an ADODB.Recordset to a VFP cursor
Procedure RS2Cursor(toRS, tcCursorName) && simple single cursor - not intended for complex ones
tcCursorName = Iif(Empty(m.tcCursorName),'ADORs',m.tcCursorName)
Local xDOM As 'MSXML.DOMDocument'
xDOM = Createobject('MSXML.DOMDocument')
toRS.Save(xDOM, 1)
Xmltocursor(xDOM.XML, m.tcCursorName)
Endproc
这是完整的代码。只需将文件路径和名称更改为您的文件路径和名称,选择所有代码,右键单击并执行选择以查看结果。或将其另存为prg,说ImportMyExcel.prg并运行它:
ImportMyExcel()
您可以看到我的结果,所以我没有上传任何结果。
此外,过程RS2Cursor(toRS,tcCursorName)是否打算生成 2个输出表?我们为什么需要此过程: ADOQuery(tcConStr,tcQuery,tcCursorName)?
对于新手来说,这些程序有些棘手(也许不是)。我认为您应该了解VFP,游标,游标适配器,将ADO记录集转换为游标等(可能是高级级别)的历史。我不知道,这些是我想出的程序,也已发布在我给您的foxite链接上。只要认为它们是黑盒功能(就像内置功能)就可以了。 ADOQuery的工作是简单地查询OLEDB源并将结果作为游标返回。使用cursorAdapter,您可能不需要这样的过程,但是该过程是在CursorAdapter存在之前设计的。
请再提出两个问题:1)m来自哪里 m.lnBalance?
m。明确通知编译器它是内存变量。它称为MDOT。有些开发人员声称不需要它,通常会导致长时间的讨论(并且您可能会在这些讨论中找到我的名字)。直到今天,没有人可以向我展示和/或演示为什么我们不应该或不需要使用它。如果您相信我,那不是偏好,而是您应该使用的东西。
2)我们是否不需要定义crsTableA?或者您是说我们可以使用 在先前的代码中创建Table tableA以使crsTableA有效吗?
不。该代码中没有表。我们将数据从excel读入游标(最初为crsTableA和crsTableB),然后将其清理为2个游标crsA和crsB。它们都是光标。游标就像表,但不会持久存储在磁盘上。他们甚至可能将一生都花在记忆中,而当您关闭它们时,它们就会消失。在这里,我首选游标,因为在不损害任何真实数据的情况下,您可以运行N次并检查结果。当您对持久化感到满意时,数据就像一个“选择...插入”或“插入...”(还有更多方法)一样简单。即使是表,也不需要使用“创建表...”。 “选择进入...”命令可以从源中选择数据,并通过创建数据将其保存到表中(就像组合的“创建表...”然后“插入到...”一样)。
此外,我看到B9:E12与tableA或tableB的范围不匹配 在我之前为您上传的Excel电子表格中。我想念吗 这里有东西吗?
如果您认为数据分别以B9和G9开头,则它与原始样本匹配。
我还有一个问题:请您说明一下这些内容 要做:在Padl(Id,10,'0')+ Padl(ord,10,'0')标签上选择CrsB索引 ALinkB选择crsA,将与Padl(Id,10,'0')的关系设置为CrsB。
我想我在上一个问题中解释了这一部分。我将很快评论代码本身。
答案 2 :(得分:0)
您有一个很长的问题,其中包含多个问题。我将尝试分批答复(在两者之间编辑我的答案),因为这将是一个漫长的答案(将其分为多个答案甚至可能会更好)。
首先,您的创建表语法接近但不正确。 VFP(不是VFB,而是V FP)不支持字段名中的空格(除非它是长字段名)。使用带空格的字段名称只会带来麻烦。因此,最好不要使用它们。看起来像:
CREATE TABLE tableA;
( id int, ;
balance double, ;
load C(240), ;
state C(240))
CREATE TABLE tableB;
( Load id int, ;
id int, ;
ord int, ;
fact double, ;
type int ;
firstValue C(240),;
roundedVal C(240), ;
state C(240))
请注意,在最后一个字段之后,您没有逗号和;在VFP中,意味着在下一行继续执行命令(因此在最后一个字段定义行中删除了该命令)。我还更改了2个字段名称,以与自由表的字段命名兼容(最大长度为10,并且必须以字母开头,不能有空格)。这样便可以更轻松地使用表格。或者,只要您一次完成就可以使用光标,因此以后不要尝试更改结构。
如果要使用长字段名,则可以像使用自由表一样执行此操作,但是该表必须是数据库的一部分。如果您一口气做到了这一点,并且以后不要尝试更改结构,那么它也适用于光标。
虽然我在其中添加了创建TableA和TableB的代码,但是您说的是这些表的数据将来自Excel。您实际上并没有提供有关Excel部分的详细信息(如何表示数据-是作为数据范围吗?)。直接通过使用ODBC / OLEDB直接从Excel中选择数据来创建这两个表的可能性很大。
为了从Excel中获取数据,我在Foxite, you can check the post in this link上发布了一些详细信息。我在这里没有提供任何示例代码,因为我还真的不了解Excel部分。
假设我们从Excel中获得数据,让我们检查其他部分(表B ID中的BTW称为外键,而不是主键。它链接了TableB顶部TableA中的行)。
1st value[i] = If(Type[i]=0, balance[i]*fact[i], balance[i]*fact[i]/(1-fact[i]))
我们可以使用REPLACE命令(xBase命令)或SQL Update命令来完成此操作。让我们不要在这里考虑它们之间的差异(确实不值得),而是选择SQL Update来完成工作(语法也可以在其他数据库中重用-例如MS SQL Server,postgreSQL,mySQL ...)。
Update tableB ;
set firstValue = iif( type = 0, ;
tableA.balance * fact, ;
tableA.balance * fact/(1-fact)) ;
from tableA ;
where tableA.Id = tableB.Id
或稍作简化:
Update tableB ;
set firstValue = tableA.balance * fact / ;
iif( type = 0, 1, (1-fact)) ;
from tableA ;
where tableA.Id = tableB.Id
请注意,VFP会每行执行一次此表达式,因此我们不需要伪代码中的[i](数组标识符)。
下一个:
rounded value[i] = If(Type[i]>0, rounddown(1st value[i], 1), roundup(1st value[i],2)
将以相同的方式翻译:
Update tableB ;
set roundVal = iif(type > 0, ;
rounddown(firstValue,1), ;
roundup(firstValue,2)) ;
from tableA ;
where tableA.Id = tableB.Id
但是,VFP没有上舍入和下舍入功能,我只是将它们写为概念性翻译。您可以做的是创建两个执行RoundUp和RoundDown的自定义函数。编写这些功能的方法有多种,恕我直言,最简单的方法是将它们编写为2个单独的.prg文件,其中,当您执行上述SQL命令时,这些prg文件位于搜索路径中:
RoundUp.prg
Lparameters tnValue, tnPlaces
If Round(m.tnValue, m.tnPlaces) = m.tnValue
Return m.tnValue
Else
Return Round(m.tnValue+((10^-(m.tnPlaces+1))*5), m.tnPlaces)
Endif
RoundDown.prg
Lparameters tnValue, tnPlaces
If Round(m.tnValue, m.tnPlaces) = m.tnValue
Return m.tnValue
Else
Return Round(m.tnValue-((10^-(m.tnPlaces+1))*5), m.tnPlaces)
Endif
您提供的链接中的功能对我来说似乎不合适(但不容易理解和测试,因此没有花时间进行彻底检查)。
我不确定一张包含两个表的表是否合适。如果Tables集合是WorkSheet或WorkBook的成员,我不记得要动弹了。如果是WorkSheet,那就可以了。我可以稍后(可能在明天)检查并编写示例代码。
您可以将逻辑类型(l)用作类型。在MS SQL Server和其他后端中,它对应于位(1或0)。内部存储为布尔值,但存储在用作.T ./。F的表达式中。 (VFP中的true \ false符号表示。在代码中,您可以简单地将其用作:
iif( type, ...
就像说iif(type = .T。,...)一样-如Type>0。并且:
iif( !type, ...
就像说iif(type = .F。,...)或iif(type NOT等于.T。,...-就像Type = 0一样。
在这种情况下,我没有使用内部联接,因为在这里使用TableA中的a就足够了(与其他后端相同,尽管一般趋势是使用联接来编写)。
编辑:添加了代码作为另一个答案。
根据您的问题: 内部联接不需要显式定义,那里有一个隐式联接。我更喜欢使用VFP的xBase功能,而不是编写SQL更新,而是使用scan ... endscan(可以与SQL一起使用,但会更复杂)。
是的,这意味着仅当主文件代码位于当前目录或搜索路径中时,才将这2个RoundUp.prg和RoundDown.prg文件放入BUT上方我们主文件代码的同一目录路径中。为了更加清楚,请考虑:
c:\ SomeFolder \ RoundUp.prg c:\ SomeFolder \ RoundDown.prg c:\ ANOTHERFolder \ Main.prg
,您在: c:\ YetAnotherFolder
如果您这样调用main.prg:
do ('c:\ANOTHERFolder\Main.prg')
它需要找到RoundUp,RoundDown,并且如果SET('PATH')中包含c:\ Somefolder,也可以找到它-例如:
Set path to c:\SomeFolder;c:\VFPHomeFolderMaybe
或者,如果您不想考虑路径,则可以将那些RoundUp \ Down代码作为过程包括在代码中(就像我在其他答案中的代码中所做的那样-请注意,在VFP中,PROCEDURE之间没有区别)和FUNCTION。您可以自由选择其中的任何一个。一些开发人员更喜欢对返回值的函数使用FUNCTION-但实际上,任何PROCEDURE \ FUNCTION都返回值,所以我们说那些用于返回值的函数。)>
我不认为逻辑类型自动表示“ 1”或“ 0”,对吗?如果 就是这种情况,我将其保留为int类型,因为 类型列的输入始终定义为1或0。
嗯,这很难正式回答。在VFP布尔数据中 类型由文字.F定义。和T。您可以将(aBoolean转换为int)并分别获得0和1。或者,您可以强制转换(1为逻辑)以获取.T。 IOW 1 \ 0和.T..F。在某种意义上是可以互换的。这完全取决于您要在哪里使用它。如果数据来自外部源,则将以1 \ 0的形式输入。只需将其强制转换或放入逻辑逻辑数据类型的列(隐式强制转换),就将其视为.T..F。或者,您是将数据从逻辑发送到外部源(例如XML,MS SQL Server,postgreSql,其他OLEDB \ ODBC数据源),然后是.T..F。强制转换为1 \ 0。