【问题标题】:Beckhoff - Build query from results of another queryBeckhoff - 从另一个查询的结果构建查询
【发布时间】:2019-04-10 16:01:28
【问题描述】:

我想根据 Twicat3 PLC 环境中的查询结果构建一个 SELECT 语句。

当变量被硬编码时查询可以正确执行,但当变量从另一个查询的结果传递时无法执行。定义的数据类型是相同的。 InformationLog 视图中的错误表示“字典中不存在给定的键”。当使用第一个查询的结果执行查询时。

PROGRAM MAIN
VAR
conv_QueryDB    : Query_tblConveyorQueue;
conv_queryRes   : ARRAY [0..9] OF tblConveyorQueue;
conv_str_query  : STRING(2000);
conv_query      : WSTRING(2000);
conv_query1     : WSTRING(255) := "SELECT TOP 1 LEFT(AreaID,5) FROM ConveyorQueue WHERE CartonID = '";
conv_barcode    : WSTRING(255) := "900981-1";
conv_query2 : WSTRING(255) := "' ORDER BY AreaID";

zone_QueryDB    : Query_tblZoneMap;
zone_queryRes   : ARRAY [0..18] OF tblZoneMap;
zone_str_query  : STRING(2000);
zone_query  : WSTRING(2000);
zone_query1 : WSTRING(255) := "SELECT transfer, zone, direction, neighborZone, deliveryLoc FROM tblZoneMap WHERE transfer = 1 AND zone = 0 AND deliveryLoc = '";
zone_dest       : WSTRING(255);
zone_query2 : WSTRING(255) := "' ";
END_VAR

conv_query := WCONCAT(conv_query1, WCONCAT(conv_barcode, conv_query2));
conv_str_query := WSTRING_TO_STRING(conv_query);
conv_QueryDB(queryString := conv_str_query, dbConn := 2, bConnect := 1);
conv_queryRes := conv_QueryDB.queryResults;

zone_dest := STRING_TO_WSTRING(conv_queryRes[0].AreaID); 
//zone_dest := "P1030"; //This works when uncommented??!!??
zone_query := WCONCAT(zone_query1, WCONCAT(zone_dest, zone_query2));
zone_str_query := WSTRING_TO_STRING(zone_query);
zone_QueryDB(queryString := zone_str_query, dbConn := 1, bConnect := 1);
zone_queryRes := zone_QueryDB.queryResults;


我希望 zone_queryRes 在执行 zone_dest 作为硬编码值时与从 conv_queryRes 数组中提取时相同。我不确定这是否是数据类型问题。执行时,查询字符串似乎具有正确的语法。

-------------编辑-------------------------------- ----------------------------------

包括 SQL 功能块。

FUNCTION_BLOCK Query_tblZoneMap
VAR_INPUT 
    queryString     : STRING(2000); //Select statement
    dbConn          : UDINT; //Int of connection configured in TF6420
    bCONNECT        : BOOL; //Rising trigger to connect to db. Need to make this re-fireable

END_VAR
VAR_OUTPUT
    queryResults    : ARRAY [0..18] OF tblZoneMap;
    nError          : INT;

END_VAR
VAR
    fbSQLDatabase   : FB_SQLDatabase(sNetID:='', tTimeout:=T#500MS);
    fbSqlCommand    : FB_SQLCommand(sNetID := '', tTimeout := T#500MS);     
    fbSQLResult     : FB_SQLResult(sNetID:='', tTimeout:=T#500MS);
    nState          : INT;
    R_TRIG1         : R_TRIG;
END_VAR


(*
    Welcome to the QueryDB function block!
    This function exists to query a database that is configured using the TF6420 Connectivity module. 
    When executed, it establishes a connection with the database, creates an instance of the FB_SQLCommand,
    executes the instance of FB_SQLCommand, then reads the cached results using FB_SQLResult into the 
    queryResults array.
    When results are received successfully, the database connection is closed.

*)
R_TRIG1(CLK:=bCONNECT);
IF R_TRIG1.Q AND nState = 0 THEN
    nState := 1;

END_IF
CASE nState OF
    0:(*Idle*)
    IF bConnect THEN
        bCONNECT := 0;
    END_IF  
    1: // Connect to database 
    IF fbSqlDatabase.Connect(dbConn) THEN
        IF fbSqlDatabase.bError THEN
            nState := 255;
        ELSE
            nState := nState+1;
        END_IF
    END_IF  

    2: // Create a command reference
    IF fbSqlDatabase.CreateCmd(ADR(fbSqlCommand)) THEN
        IF fbSqlDatabase.bError THEN
            nState := 255;
        ELSE
            nState := nState+1;
        END_IF
    END_IF

    3://Execute SELECT Statement FB_SQLCommand.ExecuteDataReturn(pSQLCmd:= , cbSQLCmd:= , pSQLDBResult:= )
    IF fbSQLCommand.ExecuteDataReturn(pSQLCmd:= ADR(queryString)
                                    , cbSQLCmd:= SIZEOF(queryString)
                                    , pSQLDBResult:= ADR(fbSqlResult)) THEN
        IF fbSQLCommand.bError THEN
            nState := 255;
            ELSE
            nState := nState + 1;
        END_IF
    END_IF

    4://Read Results 
    IF fbSqlResult.Read(nStartIndex:= 0
                        ,nRecordCount:= 19
                        ,pData:= ADR(queryResults)
                        ,cbData:= SIZEOF(queryResults)
                        ,bWithVerifying:= FALSE
                        ,bDataRelease:= FALSE) THEN
        IF fbSqlResult.bError THEN
            nState := 255;
        ELSE
            nState := nState+1;
        END_IF
    END_IF  
    5:// disconnect from database
    IF fbSqlDatabase.Disconnect() THEN
        IF fbSqlDatabase.bError THEN
            nState := 255;
        ELSE
            nState := 0;
            bConnect := 0;
        END_IF
    END_IF
    255:
        nError  := 1; // If errored, check SQL logs.
        bConnect := 0;
        nState := 0;
END_CASE

【问题讨论】:

    标签: string types string-concatenation twincat


    【解决方案1】:

    你为什么首先使用 WSTRING? 使用普通字符串,这样您就不必来回转换,并且您的代码更具可读性。

    如果您将许多字符串连接在一起,请构建一个能够执行此操作的函数。 当你调用它时看起来像这样:

    F_CONCAT3(
        zone_query1,
        zone_dest,
        zone_query2,
    );
    

    我建议您使用 Beckhoff 编码约定,以便其他开发人员也可以阅读您的代码:

    https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/45035999420423563.html&id=

    您的代码可能如下所示:

    sConvQuery := F_CONCAT3(sConvQuery1, sConvBarcode, sConvQuery2);
    
    //Use Enums instead of numbers 
    fbQueryDB(queryString := sConvQuery , dbConn := 2, bConnect := TRUE);
    aConvQueryRes := fbQueryDB.queryResults;
    
    sZoneDest := aConvQueryRes[0].AreaID; 
    sZoneQuery := F_CONCAT3(sZoneQuery1,sZoneDest,sZoneQuery2);
    
    fbQueryDB(queryString := sZoneQuery, dbConn := 1, bConnect := TRUE);
    aZoneQueryRes := fbQueryDB.queryResults;
    

    能否请您发布 zone_QueryDB 功能块的代码?

    【讨论】:

    • 感谢您的回复!代码约定是由其他人设定的。我无法控制那个。我使用 WSTRING 清理查询字符串构建,并在构建的字符串 ex "SELECT * FROM tblZoneMap WHERE col = 'sVar' " 中包含单引号。将编辑主帖以包含 QueryDB 功能块。
    【解决方案2】:

    我还认为您应该将查询按步骤顺序排列,以避免 Query_tblZoneMap 功能块中的任何冲突:

    CASE nStep OF
    
        eFIRST_QUERY: 
    
            sConvQuery := F_CONCAT3(sConvQuery1, sConvBarcode, sConvQuery2);
            //Use Enums instead of numbers 
            fbQueryDB(queryString := sConvQuery , dbConn := 2, bConnect := TRUE);
            nStep := eWAIT_FOR_FIRST_RESULT;
    
        eWAIT_FOR_FIRST_RESULT:     
    
            IF NOT fbQueryDB.bBusy THEN
                aConvQueryRes := fbQueryDB.queryResults;
                nStep := eSECOND_QUERY;
            END_IF
    
            //Error handling 
            //ELSIF fbQueryDB.bError ...
    
        eSECOND_QUERY:
    
            sZoneDest := aConvQueryRes[0].AreaID; 
            sZoneQuery := F_CONCAT3(sZoneQuery1,sZoneDest,sZoneQuery2);
            fbQueryDB(queryString := sZoneQuery, dbConn := 1, bConnect := TRUE);
            nStep := eWAIT_FOR_SECOND_RESULT;
    
        eWAIT_FOR_SECOND_RESULT:    
    
            IF NOT fbQueryDB.bBusy THEN
                aZoneQueryRes := fbQueryDB.queryResults;
                nStep := eCHECK_RESULT;
            END_IF
    
            //Error handling
            //ELSIF fbQueryDB.bError ...
    
        eCHECK_RESULT: //...    
    
    END_CASE
    

    【讨论】:

    • 这是一个很好的建议,谢谢。查询始终如一地执行。我在这里遇到的问题涉及将结果从一个查询传递到另一个查询。我可以查询一个 MSSQL 表并返回一个 char(20) 字符串 ex 'Zone14'。当我对 'Zone14' 进行硬编码时,第二个查询会返回所需的结果。当我将结果从 MSSQL 传递给 SQLite 查询时,它没有返回任何东西。您对差异有什么想法吗?有问题的变量是zone_dest
    • 如果您按照我向您展示的那样重构了代码并且仍然存在问题,那么在 sZoneQuery := F_CONCAT3(sZoneQuery1,sZoneDest,sZoneQuery2); 处切换断点;并查看第二个查询字符串。
    猜你喜欢
    • 2014-08-18
    • 2011-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-28
    • 2010-10-31
    • 1970-01-01
    • 2011-09-16
    相关资源
    最近更新 更多