I'm trying to collect data from joining table and currently there is a null displaying (no error found). I think my code isn't efficient tough. But here it is what I'm trying:
conn.Open()
sqlcmd = New MySqlCommand("select tabsen.id_absen, tsiswa.NIS, tsiswa.nama, tabsen.alpa, tabsen.izin, tabsen.sakit, tabsen.tahun_ajaran from tabsen join tsiswa on tabsen.NIS = tsiswa.NIS where tsiswa.NIS like '%" & txtnis.Text & "%'", conn)
dr = sqlcmd.executereader()
dr.Read()
If dr.HasRows Then
txtid.Text = dr.Item("id_absen")
txtnis.Text = dr.Item("NIS")
txtnama.Text = dr.Item("nama")
txta.Text = dr.Item("alpa")
txti.Text = dr.Item("izin")
txts.Text = dr.Item("sakit")
cmbtahun.Text = dr.Item("tahun_ajaran")
txta.Focus()
btnsave.Text = "UPDATE"
btndelete.Enabled = True
txtjumlah.Enabled = True
cmbpredikat.Enabled = True
cmbtahun.Enabled = True
txtnis.Enabled = False
dr.Close()
Else
While dr.Read()
txtnama.Text = dr("nama")
End While
dr.Close()
MsgBox("data absensi belum diisi")
End If
conn.Close()
If row found, it works like a charm. But when its empty on table 'tabsen', there is no error but txtnama.Text didn't show their 'nama'.
the problem
I think everyone will understand by looking the code. I can't explain much as my English isn't well enough.
... from tabsen join tsiswa on ...
This produces an Inner Join on the two tables which will only provide results if both tables have matching data.
Use either Right (Outer) Join
... from tabsen right join tsiswa on ...
Or Left (Outer) Join
... from tsiswa left join tabsen on ...
Having said that, be aware that if tabsen does not contain matching rows all of its attributes are NULL values.
So you have to check that when assigning values from the datareader to the textboxes:
Dim id_absen = dr.Item("id_absen")
txtid.Text = If(id_absen<> DBNull.Value, id_absen.ToString(), String.Empty)
...
Btw...I would recommend you set Option Strict On at the very top of your VB file. Will give you some compile errors but save you alot of mean bugs in the future.
Related
I'm having two tables which is having claims data and user name respectively
Tblimport- Contains 10 claim no.s and.
tbl_Settings- Contains 3 username
. I want to assign those 10 claims to the 3 users
Condition: same claim should not assign to different users and everyone should get the claims equally.
I'm planning to use two loops with two record set.
but problem while using do loop it's coming the same claim id to every one
strqry = "Select [claimId] from Tblimport"
strqry2 = "Select [username] from tbl_Settings"
rs1.Open strqry2, cn1, adOpenStatic
rs2.Open strqry, cn2, adOpenStatic
rs2.MoveFirst
Do While Not rs2.EOF
If rs1.EOF Then
rs1.MoveFirst
End If
claim = CStr(rs2![ClaimID])
User = CStr(rs1![UserName])
Debug.Print claim & "-" & User
rs1.MoveNext
If rs1.EOF Then
rs1.MoveFirst
End If
rs2.MoveNext
Loop
Output I'm Getting
100021245-shp
100023633-abc
114552236-kjh
In the 4th time while loop coming to User = CStr(rs1![UserName]) it's showing Run time error 94, invalid usse of null
Tblimport
100021245
100023633
114552236
121223333
122333444
112123345
111223344
112344543
322344455
tbl_Settings
shp
abc
kjh
Seems your code mismatched recordsets for the ClaimID and UserId values. Or maybe I'm just confused.
Anyway what I think you want is to move through the ClaimID values, and for each of those sequentially fetch the "next" UserId. After you've fetched the "last" UserId, move back to the first again for the following ClaimID.
Sorry I can't think how to describe that more clearly. But what it come down to is revise your looping strategy.
rs2.MoveFirst
Do While Not rs2.EOF
'Claim = CStr(rs1![ClaimID])
'User = CStr(rs2![UserId])
Claim = CStr(rs2![ClaimID])
User = CStr(rs1![UserId])
Debug.Print Claim & "-" & User
rs1.MoveNext
If rs1.EOF Then
rs1.MoveFirst
End If
rs2.MoveNext
Loop
I have two tables (lets call them Parameters 1 and 2) which both have a many-many relationship with a third table (Options). I need to group the third table records into three groups:
Those exclusively related to [specific Parameter 1 record],
Those exclusively related to [specific Parameter 2 record] and
Those related to both [specific Parameter 1 record] and [specific Parameter
2 record].
I can ignore Option records not related to either of them.
I need to be able to specify which Parameter 1 and 2 records apply in a form (using combo boxes), and have VBA juggle the three lists in the background, updating them as the Option records they contain are "used" elsewhere in the form (with check boxes).
At the risk of asking a bad question I'll submit the code I have - even though it's not a code that fails, just the framework for one that isn't even finished enough to debug yet. I simply haven't got the tools to complete it, as I don't know what methods/properties of what things to use to do it, and can't seem to find the answers in my own research thus far. Comments directing me to other resources will be appreciated, even if you don't have an answer that you're sure is best practice.
Function SetOptions()
If IsNull(cmbParam1) Or IsNull(cmbParam2) Then
MsgBox "You must select both an Param1 and a Param2!", vbCritical, "Wait!"
Exit Function
End If
'Recordsets of allowed Options
Dim Param1Opt, Param2Opt, OverlapOpt
'create recordset of tblOption.Option(s) referenced in qryPr1Opt with Param1 from cmbParam1
Param1Opt = CurrentDb.OpenRecordset("SELECT tblPr1Opt.Option FROM tblPr1Opt " &_
"WHERE Param1 = '" & cmbParam1 & "';")
'create recordset of tblOption.Option(s) referenced in qryPr2Opt with Param2 from cmbParam2
Param2Opt = CurrentDb.OpenRecordset("SELECT tblPr2Opt.Option FROM tblPr2Opt " &_
"WHERE Param2 = '" & cmbParam2 & "';")
'create recordset of tblOption.Option(s) in qryOptOvrlp with Param2 and Param1 from form
OverlapOpt = CurrentDb.OpenRecordset("SELECT qryOptOvrlp.Option FROM qryOptOvrlp " &_
"WHERE Param1 = '" & cmbParam1 & "' AND Param2 = '" & cmbParam2 & "';")
OverlapNum = Param1Num + Param2Num
'Steps remaining:
'1. Get Param1Opt and Param2Opt to only include Options not in overlap
For Each oOpt In OverlapOpt
For Each aOpt In Param1Opt
If aOpt.Value = oOpt.Value Then
'filter this record out of Param1Opt
End If
Next aOpt
For Each gOpt In Param2Opt
If gOpt.Value = oOpt.Value Then
'filter this record out of Param2Opt
End If
Next gOpt
Next oOpt
'2. Get the data in Param1Opt, Param2Opt and OverlapOpt, as well as their
'corresponding Nums to be accessible/editable in other functions/subs
End Function
You can reference the values of controls in SQL statements run in the context of Access, using the following syntax:
Forms!FormName!ControlName
or using square brackets if needed:
Forms![Form Name]!ControlName
Therefore, instead of opening multiple recordsets, you can express each of your points as a single SQL statement with joined tables. You can then either set the RowSource of a combobox or listbox to the statement (if you are only using the statement in one place); or you can save the statement as an Access query, and use the query name as the RowSource (if you need the statement in multiple places).
Given the following schema:
and two comboboxes: cmbParam1 and cmbParam2 on a form named Form1, you can use SQL statements as follows:
1. Records from Options which match tblPr1Opt but have no match in tblPr2Opt
SELECT DISTINCTROW Options.Option
FROM (Options
INNER JOIN tblPr1Opt ON Options.Option = tblPr1Opt.Option)
LEFT JOIN tblPr2Opt ON Options.Option = tblPr2Opt.Option
WHERE tblPr2Opt.Option IS NULL
AND tblPr1Opt.Param1 = Forms!Form1!cmbParam1
Or using the query designer (note the arrow head next to tblPr2Opt; this indicates a left join):
2. Records from Options which match tblPr2Opt but have no match in tblPr1Opt:
SELECT DISTINCTROW Options.Option
FROM (Options
INNER JOIN tblPr2Opt ON Options.Option = tblPr2Opt.Option)
LEFT JOIN tblPr1Opt ON Options.Option = tblPr1Opt.Option
WHERE tblPr1Opt.Option IS NuLL
AND tblPr2Opt.Param2 = Forms!Form1!cmbParam2;
or in the query designer:
3. Records from Options which match on both:
SELECT Options.Option
FROM (Options
INNER JOIN tblPr1Opt ON Options.Option = tblPr1Opt.Option)
INNER JOIN tblPr2Opt ON Options.Option = tblPr2Opt.Option
WHERE tblPr1Opt.Param1 = Forms!Form1!cmbParam1
AND tblPr2Opt.Param2 = Forms!Form1!cmbParam2
Or in the query designer:
Here's my problem:
Let's say I have these tables:
table1
1 - "a"
2 - "b"
table2
1 -
2 -
3 -
Now, I'm using the following code to compare the tables:
table2.MoveFirst
Do While Not table2.EOF
table1.Seek "=", table2!field2
If table1.NoMatch Then
go do a lot of things to find that information
Else
table2.Edit
table2!Field2 = table1!field2
table2.update
End If
table2.MoveNext
Loop
But the line
table2!Field2 = table1!field2
Is not working so well. I'm pretty sure I'm doing something wrong here, but I'm having problems finding a solution. I'm not even sure what I should google...
EDIT: Field 2 is indexed in table 1, so the 'seek' works.
A few notes.
Let us say you want all the records from Table2 where there is no match on a field called Field1:
sSQL = "SELECT Field1, FieldX FROM Table2 " _
& "LEFT JOIN Table1 " _
& "ON Table2.Field1 = Table1.Field1 " _
& "WHERE Table1.Field1 Is Null"
You could, of course, build the query in the query design window and fiddle around until is is just what you want, then switch to SQL view to get the right (ish) SQL string.
Dim rs As DAO.Recordset
Set rs = CurrentDB.Openrecordset(sSQL)
''table2.MoveFirst
Do While Not rs.EOF ''table2.EOF
''You do not need no match, all these records are missing a match
'' table1.Seek "=", table2!field2
'' If table1.NoMatch Then
go do a lot of things to find that information
rs.MoveNext
Loop
''This can all be done with one update query
'' Else
'' table2.Edit
'' table2!Field2 = table1!field2
'' table2.update
'' End If
'' table2.MoveNext
'' Loop
sSQL = "UPDATE Table2 " _
& "INNER JOIN Table1 " _
& "SET table2.Field2 = table1.field2 "
CurrentDB.Execute sSQL dbFailOnerror
Please treat the above as notes, not finished code.
But the line table2!Field2 = table1!field2 Is not working so well
...is not a good description of what goes wrong.
Does the code stop/crash at this line?
Does it run without errors, but do nothing / something else than you expected?
I suppose that you're using DAO Recordsets.
It's hard to give advice without more information, but I'll give it a try:
Is table2 completely empty? Your description looks like this:
table2
1 -
2 -
3 -
If yes, the whole loop is probably never executed at all.
Can the Recordset table2 be updated?
Not all types of Recordsets support this, it depends on how you create it. See MSDN: Recordset Object (DAO), there is a list of Recordset types at the beginning.
If it's not updateable, you should get an error when you call .Update.
If you're using DAO recordsets (as suggested by Christian) you can change the line
table2!Field2 = table1!field2
to
table2.Fields("Field2").value = table1.Fields("field2").value
I'm presuming both Field2's are of Text data type.
I was asked to create a program that inserts records into one parent table and multiple child tables. My question is, how do I know what the PK is for the parent table, so that I may add it as a FK in the child? The PK for the parent is an auto number. As I stated in my title, I'm using VB.net, mySQL, through an ODBC connection. I have to do this through the code and cannot use stored procedures. Any suggestions?
thanks
my transaction looks like this:
Dim cmdText As String = "INSERT INTO candidate(first_name, last_name, phone1, phone2, email1, city, " _
& " state, country, zip,primary_contact_id ) VALUES (?,?, ?, ?,?,?, ?,?,?,?)"
If conn.State = ConnectionState.Closed Then
conn.Open()
End If
Dim SqlStatus As Integer
Dim trans As Odbc.OdbcTransaction = conn.BeginTransaction(IsolationLevel.ReadCommitted)
Dim cmd As OdbcCommand = New OdbcCommand(cmdText, conn, trans)
Try
cmd.Parameters.Clear()
cmd.CommandType = CommandType.Text 'The default is CommandType.Text
With cmd.Parameters
.Add("#first_name", OdbcType.VarChar).Value = fName
.Add("#last_name", OdbcType.VarChar).Value = lName
.Add("#phone1", OdbcType.VarChar).Value = phone
.Add("#phone2", OdbcType.VarChar).Value = mobilePhone
.Add("#email1", OdbcType.VarChar).Value = email
.Add("#city", OdbcType.VarChar).Value = city
.Add("#state", OdbcType.VarChar).Value = state
.Add("#country", OdbcType.VarChar).Value = country
.Add("#zip", OdbcType.VarChar).Value = zip
.Add("#primary_contact_id", OdbcType.Int).Value = getContactFK
End With
SqlStatus = cmd.ExecuteNonQuery
If Not SqlStatus = 0 Then
trans.Commit()
Me.Close()
Else
MsgBox("Not Updated")
End If
Catch ex As Exception
MsgBox(ex.Message)
Finally
cmd.Dispose()
trans.Dispose()
End Try
I'm still working on the code, so not sure if it works just yet
jason
Take a look at How to Get the Unique ID for the Last Inserted Row
Since you're going through ODBC and cannot use a stored proc you will have to execute two SQL statements together (as a batch). First your insert and then SELECT LAST_INSERT_ID()
It should look something like:
INSERT INTO ... ;
SELECT LAST_INSERT_ID();
Since you're expecting a result you need to execute from your client code as a SELECT statement. And since this is a batch operation with an insert you should also consider using a transaction.
You can use
"; select last_insert_id()"
At the end of your insert for the parent table. And then use
Dim id as Integer = cint(command.ExecuteScalar())
To get the resulting key to use in the child inserts
I have a query to return random distinct rows from an Access database. Here is the query:
SELECT * FROM
(SELECT DISTINCT m.MemberID, m.Title, m.FullName, m.Address,
m.Phone, m.EmailAddress, m.WebsiteAddress FROM Members AS m INNER JOIN MembersForType AS t ON m.MemberID = t.MemberID WHERE
(Category = 'MemberType1' OR Category = 'MemberType2')) as Members
ORDER BY RND(members.MemberID) DESC
When I run this in Access it returns the rows in different order every time, as per the random sort order. When I run it through my web app however the rows return in the same order every time. Here is how I call it in my code-behind:
private void BindData()
{
using (AccessDataSource ds = new AccessDataSource("~/App_Data/mydb.mdb", GetSQLStatement()))
{
ds.DataSourceMode = SqlDataSourceMode.DataReader;
ds.CacheDuration = 0;
ds.CacheExpirationPolicy = DataSourceCacheExpiry.Absolute;
ds.EnableCaching = false;
listing.DataSource = ds.Select(new DataSourceSelectArguments());
listing.DataBind();
if (listing.Items.Count == 0)
noResults.Visible = true;
else
noResults.Visible = false;
}
}
I added in all that stuff about caching because I thought maybe the query was being cached but the result was the same. I put a breakpoint in the code to make sure the query was the same as above and it was.
Any ideas? This is driving me nuts.
When executing the ACE/Jet RND function against a new connection the same seed value is used each time. When using MS Access you are using the same connection each time, which explains why you get a different value each time.
Consider these VBA examples: the first uses a new connection on each iteration:
Sub TestDiff()
Dim con As Object
Set con = CreateObject("ADODB.Connection")
With con
.ConnectionString = _
"Provider=MSDataShape;Data " & _
"Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=C:\Tempo\Test_Access2007.accdb"
.CursorLocation = 3
Dim i As Long
For i = 0 To 2
.Open
Debug.Print .Execute("SELECT RND FROM OneRowTable;")(0)
.Close
Next
End With
End Sub
Output:
0.705547511577606
0.705547511577606
0.705547511577606
Note the same value each time.
The second example uses the same connection on each iteration (the .Open and .Close statements are relocated outside the loop):
Sub TestSame()
Dim con As Object
Set con = CreateObject("ADODB.Connection")
With con
.ConnectionString = _
"Provider=MSDataShape;Data " & _
"Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=C:\Tempo\Test_Access2007.accdb"
.CursorLocation = 3
.Open
Dim i As Long
For i = 0 To 2
Debug.Print .Execute("SELECT RND FROM OneRowTable;")(0)
Next
.Close
End With
End Sub
Output:
0.705547511577606
0.533424019813538
0.579518616199493
Note different values each time.
In VBA code you can use the Randomize keyword to seed the Rnd() function but I don't think this can be done in ACE/Jet. One workaround is to use the least significant decimal portion of the ACE/Jet the NOW() niladic function e.g. something like:
SELECT CDBL(NOW()) - ROUND(CDBL(NOW()), 4) FROM OneRowTable
I would move the RND into the inner SELECT
SELECT * FROM
(SELECT DISTINCT m.MemberID, RND(m.MemberID) as SortOrder, m.Title,
m.FullName, m.Address, m.Phone, m.EmailAddress, m.WebsiteAddress
FROM Members AS m
INNER JOIN MembersForType AS t ON m.MemberID = t.MemberID
WHERE
(Category = 'MemberType1' OR Category = 'MemberType2')) as Members
ORDER BY
Members.SortOrder DESC
You can use time as one argument to the RND field
Dim Now As DateTime = DateTime.Now
Dim millSec As Integer = Now.Millisecond
finalQuery = "SELECT * FROM wordInfo ORDER BY Rnd(-(1000* ROUND(" + millSec.ToString("N") + ", 0)) * [ID])"
So here from date and time value, millisecond value is taken which will be integer and it is used in sql query by rounding it.
wordInfo is table name
ID is the column name in database table
This gives random order every time (since millisecond value is different) be it same connection or new connection.