Skip to main content
Contributor
Jun 23, 2023 3:52 PM UTC
Solved

ID Field in Custom SQL and Setting a Unique Default for New Rows

Hi,

I have a custom SQL table for people planning and want an ID field as my primary key. 

(The existing fields that would be good for a primary key need to editable (e.g. entry date) so I cannot use a set of existing fields as my primary key.  Setting a field as a primary key makes that field not editable.) 

My question is how to set a field's default with a SQL code, "select NEWID()" which generates a unique code?  I tried creating a parameter as a Bound List and SQL code with this query but when the dashboard with the SQL table editor is opened I get a pop up window asking for an input for this parameter.  

How are primary keys normally handled in this case?  Thanks.

RobbSalzmann
Employee - Advisor
Jun 26, 2023 12:41 PM UTC

Montreux Yes, I use Dashboard Extenders for this.  Here's some example code to help you get started.  I almost never use the STE's internal save/update functionality.  Besides being fussy and undocumented, it doesn't (that I know of) stub out for data validation.

 

Private Function SaveRecords(si As SessionInfo, args As DashboardExtenderArgs) As Object
  Dim strSql As String = String.Empty
  Dim strDate As String = String.Empty
  Dim saveDataTaskResult As Object = Nothing
  Dim pKTheId As String = String.Empty
  Dim strValidationMsg = String.Empty

  Try
    'Not cancelling the default save even though nothing will be saved using the STE allows the UI to refresh with the new values
    saveDataTaskResult = New XFSqlTableEditorSaveDataTaskResult() With {
      .IsOK = True,
      .ShowMessageBox = False,
      .Message = "",
      .CancelDefaultSave = False
    }
    pKTheId = args.NameValuePairs.XFGetValue("pKTheId", "0")
    For Each row As XFEditedDataRow In args.SqlTableEditorSaveDataTaskInfo.EditedDataRows
      'Add any data validation here
      strValidationMsg = ValidateRecord(si, args, row)
      strDate = Date.Parse(row.ModifiedDataRow.Item("CustomDate")).ToString("yyyy-MM-dd")
      strSql = $"UPDATE XFW_CustomTable SET CustomAmount={row.ModifiedDataRow.Item("CustomAmount")}, Descr='{row.ModifiedDataRow.Item("Descr")}' WHERE CustomDate='{strDate}' and pKTheId={pKTheId}"
      Using DbConn As DbConnInfo = BRApi.Database.CreateApplicationDbConnInfo(si)
        BRApi.Database.ExecuteSql(DbConn, strSQL, True)
      End Using
    Next
    'We already added the new/updated records.  No need to use the SqlTableEditor to do it - so we clear them here.
        args.SqlTableEditorSaveDataTaskInfo.EditedDataRows.RemoveAll(Function(x) x.InsertUpdateOrDelete = DbInsUpdateDelType.Insert)
        args.SqlTableEditorSaveDataTaskInfo.EditedDataRows.RemoveAll(Function(x) x.InsertUpdateOrDelete = DbInsUpdateDelType.Update)

    Catch ex As Exception
         Throw New XFException($"{Environment.NewLine}{Me.GetType().ToString()}.{System.Reflection.MethodBase.GetCurrentMethod().Name}(): {ex.Message}:{Environment.NewLine}{strSql}", ex)
    End Try
  
  Return saveDataTaskResult
End Function

private function ValidateRecord(si As SessionInfo, args As DashboardExtenderArgs, row As XFEditedDataRow) As String
  Dim strValidationMsg as String = String.Empty
  'validation code here...
  
  return strValidationMsg
End Function


Private Function GetNextpKTheId(si As SessionInfo) As String
    Dim pKTheId As String = String.Empty
  Dim strSql as String = String.Empty

    Try
    strSql = "SELECT COALESCE(MAX(pKTheId), 0)+1 as NextpKTheId From XFW_CustomTable"
    Using DbConn As DbConnInfo = BRApi.Database.CreateApplicationDbConnInfo(si)
      pKTheId = BRApi.Database.ExecuteSql(DbConn, strSQL, True).Rows(0).Item("NextpKTheId")
    End Using
     Catch ex As Exception
        Throw New XFException($"{Environment.NewLine}{Me.[GetType]().ToString()}.{System.Reflection.MethodBase.GetCurrentMethod().Name}(): {ex.Message}:{Environment.NewLine}{strSql}", ex)
    End Try

    Return pKTheId
End Function

 

6 replies

Contributor
Jun 23, 2023 5:38 PM UTC

You can use GUID to have a primary key 

 

				Dim sSQL As New Text.StringBuilder
				Dim sGUID As Guid = Guid.NewGuid 'Create a new Unique ID
				
				sSQL.Append($"INSERT INTO TableName VALUES (")
				sSQL.Append($"'{sGUID}','{sUserName}',GETDATE(),'{sPOV}')")
				
				'BRApi.ErrorLog.LogMessage(si,"SQL",sql.ToString)
				
				Using DbConn
					DbConn.BeginTrans()
					BRApi.Database.ExecuteActionQuery(DbConn,sSQL.ToString,False,True)		
					DbConn.CommitTrans()
				End Using

 

 

MontreuxAuthor
Contributor
Jun 26, 2023 6:55 AM UTC

Hi, thanks for your idea.  I'm curious where this is run from?  A Dashboard Extender rule to update the table?  My concern is that if 2 identical rows are saved from the SQL table editor or Spreadsheet, the problem exists before this rule can be run to make the 2 rows unique with the snippet you've shared.  I thought setting a field's default value would be best suited for this.  Thanks for your input.

RobbSalzmann
Employee - Advisor
Employee - Advisor
Jun 23, 2023 6:46 PM UTC

If you want to create an integer ID field in your table that is in numerical order, you can use the following:

Dim strSql as String = "SELECT COALESCE(MAX(MyID), 0)+1 as MyID From MyTable"
Dim myId as Integer = 0
Using DbConn
	DbConn.BeginTrans()
	myId = BRApi.Database.ExecuteActionQuery(DbConn,strSql,False,True).Rows(0).Item("MyId")		
	DbConn.CommitTrans()
End Using
MontreuxAuthor
Contributor
Jun 26, 2023 6:58 AM UTC

Hi, thanks also for you input.  I'll ask the same question to you that I did for the other good response to my question.  Where this is run from?  A Dashboard Extender rule?  My concern is that if 2 identical rows are saved from the SQL table editor or Spreadsheet then the problem exists before this rule can be run to make the 2 rows unique.  The SQL table editor's field's default value would be best suited for this but SQL rule don't work here.  Thanks again.

RobbSalzmann
Employee - Advisor
Employee - Advisor
Jun 26, 2023 12:41 PM UTC

Montreux Yes, I use Dashboard Extenders for this.  Here's some example code to help you get started.  I almost never use the STE's internal save/update functionality.  Besides being fussy and undocumented, it doesn't (that I know of) stub out for data validation.

 

Private Function SaveRecords(si As SessionInfo, args As DashboardExtenderArgs) As Object
  Dim strSql As String = String.Empty
  Dim strDate As String = String.Empty
  Dim saveDataTaskResult As Object = Nothing
  Dim pKTheId As String = String.Empty
  Dim strValidationMsg = String.Empty

  Try
    'Not cancelling the default save even though nothing will be saved using the STE allows the UI to refresh with the new values
    saveDataTaskResult = New XFSqlTableEditorSaveDataTaskResult() With {
      .IsOK = True,
      .ShowMessageBox = False,
      .Message = "",
      .CancelDefaultSave = False
    }
    pKTheId = args.NameValuePairs.XFGetValue("pKTheId", "0")
    For Each row As XFEditedDataRow In args.SqlTableEditorSaveDataTaskInfo.EditedDataRows
      'Add any data validation here
      strValidationMsg = ValidateRecord(si, args, row)
      strDate = Date.Parse(row.ModifiedDataRow.Item("CustomDate")).ToString("yyyy-MM-dd")
      strSql = $"UPDATE XFW_CustomTable SET CustomAmount={row.ModifiedDataRow.Item("CustomAmount")}, Descr='{row.ModifiedDataRow.Item("Descr")}' WHERE CustomDate='{strDate}' and pKTheId={pKTheId}"
      Using DbConn As DbConnInfo = BRApi.Database.CreateApplicationDbConnInfo(si)
        BRApi.Database.ExecuteSql(DbConn, strSQL, True)
      End Using
    Next
    'We already added the new/updated records.  No need to use the SqlTableEditor to do it - so we clear them here.
        args.SqlTableEditorSaveDataTaskInfo.EditedDataRows.RemoveAll(Function(x) x.InsertUpdateOrDelete = DbInsUpdateDelType.Insert)
        args.SqlTableEditorSaveDataTaskInfo.EditedDataRows.RemoveAll(Function(x) x.InsertUpdateOrDelete = DbInsUpdateDelType.Update)

    Catch ex As Exception
         Throw New XFException($"{Environment.NewLine}{Me.GetType().ToString()}.{System.Reflection.MethodBase.GetCurrentMethod().Name}(): {ex.Message}:{Environment.NewLine}{strSql}", ex)
    End Try
  
  Return saveDataTaskResult
End Function

private function ValidateRecord(si As SessionInfo, args As DashboardExtenderArgs, row As XFEditedDataRow) As String
  Dim strValidationMsg as String = String.Empty
  'validation code here...
  
  return strValidationMsg
End Function


Private Function GetNextpKTheId(si As SessionInfo) As String
    Dim pKTheId As String = String.Empty
  Dim strSql as String = String.Empty

    Try
    strSql = "SELECT COALESCE(MAX(pKTheId), 0)+1 as NextpKTheId From XFW_CustomTable"
    Using DbConn As DbConnInfo = BRApi.Database.CreateApplicationDbConnInfo(si)
      pKTheId = BRApi.Database.ExecuteSql(DbConn, strSQL, True).Rows(0).Item("NextpKTheId")
    End Using
     Catch ex As Exception
        Throw New XFException($"{Environment.NewLine}{Me.[GetType]().ToString()}.{System.Reflection.MethodBase.GetCurrentMethod().Name}(): {ex.Message}:{Environment.NewLine}{strSql}", ex)
    End Try

    Return pKTheId
End Function

 

MontreuxAuthor
Contributor
Jun 26, 2023 12:51 PM UTC

This makes a lot of sense now.  Running the rule to first validate, then set the ID, then save is the order I'm looking for.  Thanks a lot!