I'm working with an external access database (.accdb) in VB.NET and I'm trying to limit the amount of times that my [Winforms] program pings the data since it slows it down pretty considerably. I figured I could do this by querying my in-memory dataset instead of continuously going back to the database itself. Though, unfortunately, I cannot seem to figure out how to query my in-memory dataset. I tried looking up LINQ but couldn't find any instructions on how to set it up.
Am I missing something? Is there a better way to do this?
Thanks so much! My starting code below...
locsql = "SELECT * FROM TABLE1)
Dim objAdapter As New OleDb.OleDbDataAdapter(locsql, objconn)
objAdapter.Fill(locdata, "BASE")
So I can easily do some basic things I need with locdata("BASE").rows.item("Item") but I have to do some stuff like
SELECT thing FROM TABLE1 WHERE this = this and that = that and I would just rather not keep going back to the database if possible.
Is it possible?
You need to use the Select method on each datatable you want to query. The syntax is very similar to the part after the WHERE clause in you normal SQL query. IE
locdata.tables(0).Select("ID = 47")
This returns an array of datarows.
If you want to use Linq against a DataTable you need to apply the AsEnumerable method then apply the usual Linq methods.
For example.
If you need to find all the customers having the Field Country equals to 'Canada' and then Sum the total of their Amount column you could write
Dim listOfCanadians = locdata.Tables(0).AsEnumerable() _
.Where(Function(row) row.Field(Of String)("Country") = "Canada") _
.ToList()
Dim totalCanadaAmount = listOfCanadians.Sum(Function(row) row.Field(Of Decimal)("Amount"))
What I ended up doing was breaking down my data filters and creating new tables based on a series of loops and if/else statements. all of these responses were very helpful, just didn't seem to work for what I needed. Thanks everyone!
Related
I have a problem using sqlQuery to connect database with R.
library(RODBC)
res =sqlQuery(channel,
paste0("select pb.col1,pb.col2 from pb,
mp,fb,st
where fb.col10 in ('%s',input),
and fb.col20=mp.col30
and pb.col40=st.col50
and pb.col45=st.col60
and mp.col40=pb.col80 and
pb.col80=st.col90"),
believeNRows=F)
Here, input=c("abc","def","wvy","etz"), but the real input has more than 10,000 string elements.
Channel is already set up for connecting with the database.
It looks like there are some problems with where-clause but I do not know how to fix it.
Can anyone help me with this?
paste0 does not work the way you are using it. You would need to use:
sprintf("select pb.col1,pb.col2
from pb,mp,fb,st
where fb.col10 in %s
and fb.col20=mp.col30
and pb.col40=st.col50
and pb.col45=st.col60
and mp.col40=pb.col80 and
pb.col80=st.col90", input)
Next, the way you have this structured will result in the query argument being a vector. You should aim to have query be a single string.
You might be better off using RODBCext
library(RODBCext)
res =sqlExecute(channel,
"select pb.col1,pb.col2
from pb,mp,fb,st
where fb.col10 in ?,
and fb.col20=mp.col30
and pb.col40=st.col50
and pb.col45=st.col60
and mp.col40=pb.col80
and pb.col80=st.col90",
data = list(c("abc", "def", "wvy", "etz")),
fetch = TRUE,
stringsAsFactors = FALSE)
Lastly, I'm not sure this query is valid SQL syntax. Maybe I'm mistaken, but I don't think you can list multiple tables in the FROM clause like you have here. If you need multiple tables, there should be some way of joining them.
FROM Table1 LEFT JOIN Table2 ON Table1.ID = Table2.Ref
EDIT: I just saw that your input has over 10,000 elements, which will make sqlExecute pretty slow. Are you sure a LIKE is the best way to query these data. If possible, I would recommend some other approach to isolating the data that you need.
Coming from years of web development where PHP and SQL statements were so simple, this recent task I've been required to undergo with MS Access and VBA is absolutely doing my head in at how much it complicates SQL statements. Mind you I have no prior knowledge about VBA so it could be extremely simple and I'm not just getting it, but all I want to do is
"SELECT type FROM tblMatter WHERE id='$id'"
When I wear my PHP cap, I want to think okay, we are going to have one row of data, that's going to be an array, and I want one object out of that array. Simple.
VBA, however, complicates the $#!t out of it. So far my code looks something like this
Dim matterSQL As String
Dim matterRS As Recordset
matterSQL = "SELECT type FROM tblMatter WHERE id'" & id & "'"
Set matterRS = CurrentDb.OpenRecordset(matterSQL)
MsgBox matterRS![type]
CurrentDb is defined much much earlier in the code to open the connection to the database, and the error is on the line containing OpenRecordset with the error: Data type mismatch in criteria expression.
As I said, I'm new to VBA so I don't know what the heck I'm doing, and all the documentation on the internet is nowhere near helpful. But all I want to do is to get one piece of data from the table.
Thanks in advance!
Edit: I needed to build upon this with another query that takes the info from the last query to run. Same kind of ordeal:
Dim costSQL As String
Dim costRS as Recordset
costSQL = "SELECT email FROM tblScaleOfDisb WHERE category=" & category
Set costRS = CurrentDb.OpenRecordset(costSQL)
MsgBox costRS![email]
This time I'm getting an error on the line containing OpenRecordset with the error: Too few parameters. Expected 1.
Which I don't understand because the code is practically the same as the first half of the question. What have I done wrong?
You are missing = in the condition
try below
matterSQL = "SELECT type FROM tblMatter WHERE id='" & id & "'"
Also if id is numeric you don't need '
matterSQL = "SELECT type FROM tblMatter WHERE id=" & id
Too few parameters. Expected 1.
This happens when the field name in your sql query do not match the table field name
if the field name are correct i believe the the datatype of category is not numeric then you have to use '
costSQL = "SELECT email FROM tblScaleOfDisb WHERE category='" & category &"'"
Always try to use parameterised query to avoid SQL injection
You must understand or prepare few things before you start coding on a new platform. such as
Using Keywords/ Reserved keywords
Capturing Errors
basic arithmetic operations/ string operations.
Available functions / methods
Ways of cleaning your variables after using it
in your case you also need to learn about MS ACCESS SQL. Which is pretty similar to standard SQL but (limited to and) strongly influenced by MS Access internal functions.
SQL execution will return n Rows as result. Each row will have n number of columns. You need to understand how you need to loop through result sets.
Please do have some error capturing method. I will help you to understand the direction before spending hours in Google.
in your first SQL: you have a reserved keyword Type. use square brackets to escape reserved keywords. In where condition numeric fields must not have string quotes and strings must have them.
Tip: You can use the MS Access visual query builder to build your query and copy the SQL to VBA.
list of reserved keywords in MS ACCESS: https://support.microsoft.com/en-us/kb/286335
list of functions: http://www.techonthenet.com/access/functions/
Error handling : http://allenbrowne.com/ser-23a.html
Clean/close your objects after usage by explicitly setting as nothing: Is there a need to set Objects to Nothing inside VBA Functions
We have a linq query as shown below to display items first it should display records matching with the text and next with if the text contains in the string.But it is causing lot of performance issue. Could any one pls help how to improve it.
Dim result1 = From entry As IAutoCompleteEntry In oldList
Where entry.ToString.ToUpper.Contains(Me.Text.ToUpper())
Order By entry.ToString.IndexOf(Me.Text.ToUpper()), entry.ToString.ToUpper.StartsWith(Me.Text.ToUpper())
Descending
Select entry
You are calling ToUpper() at several places. Is it possible to get your list/array to have a ToUpper() before you get into this linq query?
I think you can also have another column for entry.ToString.IndexOf(Me.Text.ToUpper()), entry.ToString.ToUpper.StartsWith(Me.Text.ToUpper()) before getting to this linq query.
It might improve on performance....
First, I'm not sure what the significance of the .StartWith has. Since you are already getting the IndexOf, you would already know the answer of StartsWith (it should be 0).
Next, you really shouldn't be using ToUpper (as #RobertMcKee mentioned), instead you should be using case insensitive comparisons. In that case, you shouldn't need the .ToUpper's anymore either...
Finally, I was actually going to say use contains for your second statement, but you probably don't even need it. You could just sort descending based on your entry variable. Here is an updated query I wrote up:
dim result1 = From entry As IAutoCompleteEntry In oldList
Where (entry.IndexOf(Me.Text, StringComparison.OrdinalIgnoreCase) <> -1)
Order By entry Descending
Select entry
My company has just started using LINQ and I still am having a little trouble with the abstractness (if thats a word) of the LINQ command and the SQL, my question is
Dim query = (From o In data.Addresses _
Select o.Name).Count
In the above in my mind, the SQL is returning all rows and the does a count on the number rows in the IQueryable result, so I would be better with
Dim lstring = Aggregate o In data.Addresses _
Into Count()
Or am I over thinking the way LINQ works ? Using VB Express at home so I can't see the actual SQL that is being sent to the database (I think) as I don't have access to the SQL profiler
As mentioned, these are functionally equivalent, one just uses query syntax.
As mentioned in my comment, if you evaluate the following as a VB Statement(s) in LINQPad:
Dim lstring = Aggregate o In Test _
Into Count()
You get this in the generated SQL output window:
SELECT COUNT(*) AS [value]
FROM [Test] AS [t0]
Which is the same as the following VB LINQ expression as evaluated:
(From o In Test_
Select o.Symbol).Count
You get the exact same result.
I'm not familiar with Visual Basic, but based on
http://msdn.microsoft.com/en-us/library/bb546138.aspx
Those two approaches are the same. One uses method syntax and the other uses query syntax.
You can find out for sure by using SQL Profiler as the queries run.
PS - The "point" of LINQ is you can easily do query operations without leaving code/VB-land.
An important thing here, is that the code you give will work with a wide variety of data sources. It will hopefully do so in a very efficient way, though that can't be fully guaranteed. It certainly will be done in an efficient way with a SQL source (being converted into a SELECT COUNT(*) SQL query. It will be done efficiently if the source was an in-memory collection (it gets converted to calling the Count property). It isn't done very efficiently if the source is an enumerable that is not a collection (in this case it does read everything and count as it goes), but in that case there really isn't a more efficient way of doing this.
In each case it has done the same conceptual operation, in the most efficient manner possible, without you having to worry about the details. No big deal with counting, but a bigger deal in more complex cases.
To a certain extent, you are right when you say "in my mind, the SQL is returning all rows and the does a count on the number rows". Conceptually that is what is happening in that query, but the implementation may differ. Compare with how the real query in SQL may not match the literal interpretation of the SQL command, to allow the most efficient approach to be picked.
I think you are missing the point as Linq with SQL has late binding the search is done when you need it so when you say I need the count number then a Query is created.
Before that Linq for SQL creates Expression trees that will be "translated" in to SQL when you need it....
http://weblogs.asp.net/scottgu/archive/2007/05/19/using-linq-to-sql-part-1.aspx
http://msdn.microsoft.com/en-us/netframework/aa904594.aspx
How to debug see Scott
http://weblogs.asp.net/scottgu/archive/2007/07/31/linq-to-sql-debug-visualizer.aspx
(source: scottgu.com)
Well I am querying my DB, a table called bookBilling, to get a value under the column of billingID.
In my first query I get the customer ID from a table based on what value the cookie holds.
In my second query I take that custID value and am looking to get the billingID associated with it.
query = "SELECT custID FROM bookSession WHERE session='"&theCookie&"'"
'--Connect to DB'
Set objConn = ConnectDB()
'--Query to DB'
Set objRS = objConn.Execute(query)
custID = objRS.Fields("custID")
query = "SELECT billingID FROM bookBilling WHERE custID="&custID&""
objConn.Execute(query)
Here is where it becomes an issue. I tried using this to obtain the value:
billingID = objRS.Fields("billingID")
When that didn't work I printed the query to my browser to make sure it worked and it came back good, I checked it in SQL Server Management Studio to make sure. After that I decided to play with it a bit and found that using "0" as the reference works since it is the first item in my table.
billingID = objRS.Fields(0)
So that worked, I know my table column header is correct I have looked at it and used it in other queries before. Does anyone know why this might happen? Is it a result of not clearing my connection or closing it afterward?
Thanks
Hey, is it a typo that you don't have this?
query = "SELECT billingID FROM bookBilling WHERE custID="&custID&""
objRS = objConn.Execute(query)
To reload the recordset with the data and definition of the second query.....
Just a thought, try Setting/Instantiating the ObjRS to a Recordset first, then apply the query.execute to it after, rather than the initial all in one with the first CustId query
Running two separate queries is slow anyway. It's almost always faster to combine them into one statement:
SELECT billingID
FROM bookBilling bb
INNER JOIN bookSession bs ON bs.custID=bb.custID
WHERE bs.session= #theCookie
Also: cookies are just text files, and anyone can edit a text file. If you substitute a cookie value directly in your query like that there is the potential for sql injection. It's not the normal attack vector, but it's still possible.
As to your specific error, you execute the 2nd query directly from the connection rather than opening it in the record set:
objConn.Execute(query)
I'm surprised you get anything at all, and I expect the value you're seeing in objRS.Fields(0) is probably just the custID from the previous query. But that all becomes moot if you consolidate the queries like I recommended.
query = "SELECT billingID FROM bookBilling WHERE custID="&custID&""
objConn.Execute(query)
You're not opening a resordset for that second query.
And, not sure if that has any influence at all, but I never name my queries identical. To be sure I guess.
Edit, well that's kind of what the people above me said, I'm a Slowpoke.
Damn, yes it was a typo. I'm not sure I understand why it worked using the column number still but not the column name even though I didn't open the recordset.
#Joel Coehoorn
Thanks for the info on inner joining the queries. My teacher has been telling us to do that but I have not really understood it enough to do it until I saw that.
For my table I can't really use it though because there isn't necessarily a billingID upon registration so I would run into some "EOF's" occasionally.
Thanks everyone!