如何通过 VB net(odbc 连接)使用用户定义的函数在 SQL 表达式中添加列
我是 VB.net 新手,我想在新列中转换并显示 UnixStamp 数据。
- 变体 DatGridView = 数据源是数据集。 我可以在 SQL 查询中创建空列 (DataGridView_Dataset),不幸的是我不能直接使用使用我自己的函数将数据转换为新列。错误请参阅。SQL 错误代码 该功能独立工作。看。 工作 UnixStampFunction
我在 7 秒内得到了 56,000 个句子的结果,但没有从中获取日期和时间值UnixTimeStamp
在SQL语句中使用udf有解决方案吗?
- DataGridView 变体 - odbcExecuteRead 解决方案
使用给定的代码显示例如 10 个句子不是问题(10 个句子结果),但如果记录超过大约 100 条(每月大约 5 万条),则会显示类似这样的错误 (Managed Debug Helper ContextSwitchDeadlock: The module CLR might 60 秒内不会脱离上下文 COM 0xb45680 到上下文 0xb455c8。)。
取消选中 ContextSwitchDeadlock 选项 调试>窗口> VS 2019中的异常设置 我在 228 秒内得到了 56 000 条记录的结果。
是否可以优化代码或者是否可以使用其他解决方案?
代码:
Public Class Form1
Public strDateTime, strDate, strTime As String
Public x As Integer =0
Public y As Integer =0
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
Dim dgvDT As New DataGridView
Dim odbcConn As OdbcConnection
Dim odbcComm As OdbcCommand
Dim odbcAdapter As New Odbc.OdbcDataAdapter
Dim odbcDataset As New DataSet
Dim odbcDataTable As DataTable
Dim strConn, strSQL, strSQL1 As String
dgvDT.Location = New Point(382, 2)
dgvDT.Width = 360
dgvDT.Height = 600
Me.Controls.Add(dgvDT)
strConn = "Driver=Firebird/InterBase(r) driver;User=;Password=;DataSource=...." '
odbcConn = New OdbcConnection(strConn)
odbcConn.Open()
strSQL = "SELECT TEST.UNIXTIMESTAMP, " & "'dd.mm.yyyy" & "'" & "AS Date_ , " & "'hh:mm:ss" & "'" & "AS Time_ " _
& "From TEST " _
& "Where TEST.UNIXTIMESTAMP > 1646092800 " _ '1.3.2022
& "Order By TEST.ID "
strSQL1 = "SELECT TEST.UNIXTIMESTAMP, UnixTimestampToDateOrTime(TEST.UNIXTIMESTAMP,1) As Date_, " & "'hh:mm:ss" & "'" & "AS Time_ " _
& "From TEST " _
& "Where TEST.UNIXTIMESTAMP > 1646092800 " _ '1.3.2022
& "Order By TEST.ID "
odbcComm = New OdbcCommand(strSQL, odbcConn)
'odbcComm = New OdbcCommand(strSQL1, odbcConn)
odbcAdapter.SelectCommand() = odbcComm
odbcAdapter.Fill(odbcDataset, "TEST")
odbcDataTable = odbcDataset.Tables("TEST")
dgvDT.DataSource = odbcDataTable
dgvDT.Columns(0).HeaderText = "UnixTimeStamp"
dgvDT.Columns(1).HeaderText = "Date"
dgvDT.Columns(2).HeaderText = "Time"
dgvDT.Visible = True
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message, "Error")
End Try
End Sub
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Try
Dim dgvDT1 As New DataGridView
Dim odbcConn1 As OdbcConnection
Dim odbcComm1 As OdbcCommand
Dim odbcDR As OdbcDataReader
Dim x As Integer = 0
Dim y As Integer = 0
Dim strConn1, strSQL, strSQL2 As String
dgvDT1.Location = New Point(382, 2)
dgvDT1.Width = 360
dgvDT1.Height = 600
For i As Integer = 0 To 2
Dim dgvNC As New DataGridViewTextBoxColumn
dgvNC.Name = "Column" & i.ToString
dgvDT1.Columns.Add(dgvNC)
Next
dgvDT1.Columns(0).HeaderText = "UnixTimeStamp"
dgvDT1.Columns(1).HeaderText = "Date"
dgvDT1.Columns(2).HeaderText = "Time"
dgvDT1.ReadOnly = True
dgvDT1.AllowUserToAddRows = False
dgvDT1.AllowUserToDeleteRows = False
strSQL2 = "SELECT TEST.UNIXTIMESTAMP " _
& "From TEST " _
& "Where TEST.UNIXTIMESTAMP > 1646092800 " _
& "Order By TEST.ID "
strConn1 = "Driver=Firebird/InterBase(r) driver;User=;Password=;DataSource="
odbcConn1 = New OdbcConnection(strConn1)
odbcConn1.Open()
odbcComm1 = New OdbcCommand(strSQL2, odbcConn1)
odbcDR = odbcComm1.ExecuteReader()
While (odbcDR.Read()) 'And y <= 10
dgvDT1.Rows.Add()
dgvDT1.Rows(y).Cells("Column0").Value = (odbcDR.GetValue(0).ToString())
dgvDT1.Rows(y).Cells("Column1").Value = (UnixTimestampToDateOrTime(odbcDR.GetValue(0), 1))
dgvDT1.Rows(y).Cells("Column2").Value = (UnixTimestampToDateOrTime(odbcDR.GetValue(0), 2))
y = y + 1
End While
Me.Controls.Add(dgvDT1)
dgvDT1.Visible = True
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message, "Error")
End Try
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
MsgBox(UnixTimestampToDateOrTime(1646092800, 1) & vbNewLine & UnixTimestampToDateOrTime(1646092800, 2))
End Sub
Public Function UnixTimestampToDateOrTime(ByVal _UnixTimeStamp As Long, ByRef _Parameter As Integer) As String
strDateTime = New DateTime(1970, 1, 1, 0, 0, 0).AddSeconds(_UnixTimeStamp).ToString()
strDate = strDateTime.Substring(0, strDateTime.IndexOf(" "))
strTime = strDateTime.Substring(strDateTime.IndexOf(" "), strDateTime.Length - strDateTime.IndexOf(" "))
If _Parameter = 1 Then
Return (strDate)
Else
Return (strTime)
End If
End Function
End Class
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
你写:
是的,你可以。但我不相信这会让你的应用程序更快。
不过,我将概述我个人在没有 UDF 的情况下使用的数据转换策略。
我相信,删除 UDF 总体上会增强您的数据库。但不会解决性能问题。由于信息量很大,我无法将其格式化为纯粹的评论。
那么,关于从查询中删除 UDF。
Firebird 2.5 文档链接: https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref25/firebird-25-language-reference.html#fblangref25-psql-triggers
Firebird 3 文档链接:
https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref30/firebird-30-language-reference.html firebirdsql.org/file/documentation/html/en/refdocs/fblangref30/firebird-30-language-reference.html
主要区别在于 FB3+ 具有“存储函数”和甚至是确定性的,而 FB2 - 仅是“存储过程”。
,如果您只有用于 Win64 的 UDF DLL,则不能使用用于 Linux/ARM 的 Firebird)。
我会避免在这里使用 UDF:它们被声明为过时的,它们使部署更加复杂,并且会降低您的灵活性(例如 我推测,你可以使用。
您必须找到一种算法,如何将 UNIX 日期/时间“解析”为日、月等单独的值。
您必须在
过程 SQL
存储函数中实现该算法(在 FB3 中,使其成为“DETERMINISTIC”)或存储过程(FB2,在设置输出值后,通过使用“SUSPEND”命令使其“可选择”)参数和退出前) - 请参阅 Ch.手册第 7 条。对于组装
DATE
或TIME
或DATETIME
类型的值,您必须使用integer
来字符串 (VARCHAR
) 到结果类型强制转换,请参阅第 1 章。手册 3.8。例子:
db<>fiddle 此处
您应该将转换后的列添加到您的表中。这些列 q 将是只读的,但它们会将“unix 时间”更改为 Firebird 本机日期和/或类型。
阅读第 1 章5.4.2.手册中,关于
ALTER TABLE 的内容ADD
命令。阅读 5.4.1。关于计算字段(列,使用
COMPUTED BY (<表达式>)
而不是实际数据类型声明的手册中的CREATE TABLE
。您可以使用这些列必须创建,这就是 FB3 和 FB2 之间的区别所在。在 FB3 中,我相信您将能够直接使用 PSQL 函数作为表达式。
在 FB2 中,我相信您必须使用一个相当具体的蹦床,将存储过程强制转换为表达式:
You write:
Yes you can. But i do not believe that would make your applicatio nany faster.
However I would outline the strategy i personally would had used getting data conversion without UDF.
Removing UDF, i believe, would enhance your database in general. But would not fix performance problems. Due to amount of information i could not format it as mere comment.
So, about removing UDF from the query.
Link to Firebird 2.5 documentation: https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref25/firebird-25-language-reference.html#fblangref25-psql-triggers
Link to Firebird 3 documentation:
https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref30/firebird-30-language-reference.html
The key difference would be that FB3+ has "stored functions" and even
DETERMINISTIC
ones, and FB2 - only "stored procedures".I would avoid using UDFs here: they are declared obsolete, they make deployment more complex, and they would reduce your flexibiltiy (for example, you can not use Firebird for Linux/ARM if you only have UDF DLL for Win64)
So, the approach i speculate, you can use.
You would have to find an algorythm, how to "parse" UNIX date/time into separate values for day, month, etc.
You would have to implement that algorythm in
Procedural SQL
stored function (in FB3, make itDETERMINISTIC
) or stored procedure (FB2, make it selectable by havingSUSPEND
command, after you set value for the output parameter and before exit) - see the Ch. 7 of the manual.For assembling values of
DATE
orTIME
orDATETIME
types you would have to useinteger
to string (VARCHAR
) to resulting type coersion, see Ch. 3.8 of the manual.Example:
db<>fiddle here
You would have add the converted columns to your table. Those columns qwould be read-only, but they would change "unix time" to Firebird native date and/or type.
Read Ch. 5.4.2. of the manual, about
ALTER TABLE <name> ADD <column>
command.Read 5.4.1.
CREATE TABLE
of the manual about Calculated fields ( columns, declared usingCOMPUTED BY ( <expression> )
instead of actual datatype. Those columns you would have to creat, and here is where difference between FB3 and FB2 kicks in.in FB3 i believe you would be able to directly use your PSQL Function as the expression.
in FB2 i believe you would have to use a rather specific trampoline, to coerce a stored procedure into expression:
现在,这将解决性能问题。再次,将此a写为答案,因为它太大,不适合作为评论。
SELECT
的结果列列表中使用 UDF - 这里应该不会出现性能问题。因此,您的应用程序/数据库应该存在其他瓶颈。
Where TEST.UNIXTIMESTAMP >查询中的 1646092800
和Order By TEST.ID
。明显的问题是您的表在这些列上是否有索引。如果不是,则强制数据库服务器执行完全自然扫描
以应用where
条件,然后使用外部临时文件排序。这可能非常慢,并且随着表的增长可能会严重扩展。使用您选择的数据库设计工具来检查您选择的
查询计划
。Firebird 使用索引还是非索引访问路径。
网上有文章如何阅读Firebird的查询。不过,我不会立即懂英语。另外,它会特定于您的数据库设计工具,以及如何获取它。
抱歉,没有灵丹妙药。这就是需要学习数据库的地方。
阅读文档的
数据定义语言/创建索引
章节。Firebird 2.5 文档链接: https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref25/firebird-25-language-reference.html#fblangref25-psql-triggers
Firebird 3 文档链接: https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref30/firebird-30-language-reference.html
检查您的数据库结构,如果
UNIXTIMESTAMP
和ID
是否有索引。一般来说,索引会加快一些(不是全部)读取查询的速度,并会(稍微)减慢所有写入查询的速度。如果这些索引尚不存在,您可能会决定添加它们。
同样,这取决于您的数据库设计工具,如何检查现有索引。它还取决于您的数据和应用程序是否需要哪种索引。这不是其他人可以为你决定的。
通常桌面应用程序仅读取前 100 行左右。人们很少会在第一页或两页之后真正阅读任何内容。人类不是机器。
尝试以某种方式将视觉网格连接到
select
查询,而不读取所有表格。应该有办法。此外,还有免费的 Firebird .Net Provider - 它应该与 VB.Net 一起工作,并且应该是您的首选,而不是 ODBC。
还有商业 IBProvider,基于本机 OLE DB 技术,它应该是比 .Net Provider 更糟糕的选择,但它也可以工作,并且有一些 VB.Net 的代码示例,我想它仍然是比 ODBC 驱动程序维护得更好。
Now, this would address the perfomance problem. Again, writing this aas an answer, because it is too large to fit as a comment.
SELECT
- and here should be no prerformance troubles.Your application/database therefore should have other bottlenecks.
Where TEST.UNIXTIMESTAMP > 1646092800
andOrder By TEST.ID
in your query. The obvious question is if your table does haveindex
on those columns. If not, you force the database server to dofull natural scan
to applywhere
condition, and then use external, temporary file sorting. THAT can be really slow, and can scale badly as the table grows.Use your database design tool of choice to check
query plan
of your select.Does Firebird use indexed or non indexed access paths.
There are articles online how to read Firebird's query. I don't instantly know English language ones though. Also, it would be specific to your database design tool, how to get it.
Sorry, there is no silver bullet. That is where learning about databases is required.
Read
Data Definition Language / Create Index
chapter of the documentation.Link to Firebird 2.5 documentation: https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref25/firebird-25-language-reference.html#fblangref25-psql-triggers
Link to Firebird 3 documentation: https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref30/firebird-30-language-reference.html
Check your database structure, if
UNIXTIMESTAMP
andID
have index on them or not. In general, indices speed some (not all) reading queries and slow down (slightly) all writing queries.You may decide you want to add those indices if they do not exist yet.
Again, it would be dependent upon your database design tool, how to check for existing of the indexes. It also would depend on your data and your applications which kind of indexes is needed or not. That is not what someone else can decide for you.
odbcAdapter.Fill(odbcDataset, "TEST")
command. Basically, you try to read all the data in one go. And you do it via ODBC connection, that is not natural for C#.Usually desktop application only read first 100 or so rows. People would rarely actually read anything after first page or two. Humans are not machines.
Try to somehow connect your visual grid to the
select
query without reading ALL the table. There should be way.Additionally, there is free Firebird .Net Provider - this should work natively with VB.Net and should be your first choice, not ODBC.
There also is commercial IBProvider, based on native OLE DB technology it should be worse choice than .Net Provider, but it can work too and has some code examples for VB.Net, and i suppose it is still better mantained than ODBC driver.