连接两个表的数据计算

时间:2018-09-22 09:23:12

标签: visual-foxpro foxpro

我正在研究Foxpro,以创建一个简单的应用程序来处理两个表AB(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 valuerounded valueload的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中的结果,我们对原始tableAtableB进行如下更新:

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 valuerounded valueload的结果并将其存储到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), ;)

3 个答案:

答案 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

编辑:我编辑了下面答案的另一个答案。现在您的问题:

  
      
  1. 不要在“过程过程”之后调用GetDataFromExcel(“ c:\ myFolder \ myExcel.xlsx”,“ B9:E12”,“ G9:N19”)   GetDataFromExcel(tcExcelFileName,tcTableARange,tcTableBRange)??
  2.   

不。过程始终放在prg文件中的常规执行代码之后。 IOW,如果您的PRG具有:

Do Something
* ...

Procedure SomeProcedure
* ...
endproc

Procedure Something
endproc

代码从调用Something开始,然后执行代码,直到看到第一个Procedure调用(或FUNCTION,DEFINE CLASS)。可能是一个过程(如示例中所述)或单独的程序。

  
      
  1. 不应在roundDown(firstValue,1),之前调用Procedure roundUp和Procedure roundDown; roundUp(firstValue,2))??
  2.   

否,与上述相同。您所说的更多内容看起来像核心C的规则。

  
      
  1. 此行中的左ID是否在ID = crsA.Id时扫描来自CrsB?另外,为什么从crsA变为CrsA?这是一个   错字? – user177196 5分钟前
  2.   

是的。它来自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。