Select all data from 1 column - sql

I have code that is supposed to take all "course names" from a module based on what school is chosen.
For example the school of biology has the modules "biochemistry" and "marine biology". I need to select both of these in order to use in a calculation.
Currently when the code is run it will only take the first module ie "biochemistry" but I need it to select all of the modules.
Can anyone help?
Dim courseSelectCom As New SqlCommand("SELECT course_name FROM course where school= '%"schoolSelect & "' ", _
connection)

The first thing to do on your query is to use a parametrized query.
This will avoid Sql Injection and parsing problems.
Then you shoud explain why you use the wildcard % in front of SchoolSelect.
Do you have many kind of schools that ends with the same suffix and do you want to retrieve all of them?
Last, you should use a DataReader to loop over the returned data or a DataAdapter to fill a DataTable.
So summarizing
Dim sb = new StringBuilder()
Using connection = New SqlConnection("your_con_string_here")
connection.Open()
Dim courseSelectCom = New SqlCommand("SELECT course_name FROM course where school=#schoolName", connection)
courseSelectCom.Parameters.AddWithValue("#schoolName", SchoolSelect.Text)
Dim reader = courseSelectCom.ExecuteReader()
while reader.Read()
sb.AppendLine(reader("course_name")) ' or add to some kind of list to reuse'
End While
End Using
Console.WriteLine(sb.ToString())
if you want to store the data in a DataTable then replace the DataReader loop with
Dim dt = New DataTable()
Dim da = New SqlDataAdapter(courseSelectCom)
da.Fill(dt)
And you have a DataTable object to use for binding to a DataGridView, Listbox or whatever....

What are you using for Executing the command?
If you use ExecuteScalar you will get only first result.
Use ExecuteReader and read through or Use DataAdapter and fill a DataSet with it.

Dim courseSelectCom As New SqlCommand("SELECT course_name FROM course where school=#School", _connection)
courseSelectCom.Parameter.Add("#School", SqlDbType.VarChar).Value = SchoolSelect.Text
SchoolSelect is the textbox from which you select school

If you want all the courses in a comma delimited list, then use group_concat (assuming you are using MySQL):
SELECT group_concat(course_name)
FROM course
where school= '%"schoolSelect & "'
This returns one row, with all the courses in a single column, like 'biochemistry,marine biology'.
To do this in SQL Server, you can do:
select stuff((select ','+course_name from course where school= '%"schoolSelect & "' for xml path ('')),
1, 1, '') as coursenames
SQL Server does not have a native aggregate string concatenation operator, so this uses XML features of the database.
I think it would be written as:
Dim courseSelectCom = New SqlCommand("select stuff((select ','+course_name from course where school= '%"schoolSelect & "' for xml path ('')), 1, 1, '') as coursenames", connection)
You need something like this:
Dim reader = courseSelectCom.ExecuteReader()
reader.Read()
reader("course_names")

Related

Filter between dates VB.NET and Access database

As the title says, I'm unable to filter an SQL sentence from access database with vb.net
Dim data1 As String = DateTimePicker1.Value.ToShortDateString
Dim data2 As String = DateTimePicker2.Value.ToShortDateString
Dim sql As String = "SELECT totais.* From totais Where totais.data Between #" + data1 + "# And #" + data2 + "#;"
It gives me random values. If i put 1-10(October)-2019 it gives me all the records in system, if i put 12-10(October)-2019 it only gives today's record (doesn't show yesterday and before records). I'm not finding the problem, can you please help?
Thanks
I would use Parameters instead of concatenating a string for the Sql statement. It makes the statement much easier to read and avoids syntax errors.
With OleDb the order that parameters appear in the sql statement must match the order they are added to the parameters collection because OleDb pays no attention to the name of the parameter.
Private Sub OPCode()
Dim sql As String = "SELECT * From totais Where data Between #StartDate And #EndDate;"
Using dt As New DataTable
Using cn As New OleDbConnection("Your connection string"),
cmd As New OleDbCommand(sql, cn)
cmd.Parameters.Add("#StartDate", OleDbType.Date).Value = DateTimePicker1.Value
cmd.Parameters.Add("#EndDate", OleDbType.Date).Value = DateTimePicker2.Value
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
DataGridView1.DataSource = dt
End Using
End Sub
You need to use single quotes and convert type in SQL like this:
SELECT totais.* FROM totais WHERE totais.data Between CDATE('" + data1 + "') And CDATE('" + data2 + "');"
You should use parameters as per Mary's answer BUT for completeness...
Ms/Access requires dates specified as #mm/dd/yy# so your SQL will only work properly where the local date time format is mm/dd/yy. i.e. mostly the US. Otherwise you will have to format your date string.

Naming Column Header Based On Results From Database

net and would to have the Header Text of columns in a datagridview be named after results from the database, e.g the query in my code returns four dates,30/08/2017,04/09/2017,21/09/2017 and 03/02/2018. My aim is to have the column headers in the data grid named after those dates. Your help will highly be appreciated.
sql = "SELECT COUNT (ServiceDate) As NoOfServiceDates FROM (SELECT DISTINCT ServiceDate FROM tblattendance)"
Using command = New OleDbCommand(sql, connection)
Using reader = command.ExecuteReader
reader.Read()
ColumnNo = CInt(reader("NoOfServiceDates")).ToString
End Using
End Using
DataGridView1.ColumnCount = ColumnNo
For i = 0 To DataGridView1.Columns.Count - 1
sql = "SELECT DISTINCT ServiceDate FROM tblattendance"
Using command = New OleDbCommand(sql, connection)
Using reader = command.ExecuteReader
While reader.Read
DataGridView1.Columns(i).HeaderText = reader("ServiceDate").ToString
End While
End Using
End Using
Next
The current code re-runs the query each time through the column count loop, meaning it will set the column header for that column to all of the date values in sequence, so the last value in the query shows in the all the columns. You only need to run the query once:
Dim i As Integer = 0
sql = "SELECT DISTINCT ServiceDate FROM tblattendance"
Using command As New OleDbCommand(sql, connection), _
reader As OleDbDatareader = command.ExecuteReader()
While reader.Read
DataGridView1.Columns(i).HeaderText = reader("ServiceDate").ToString
i+= 1
End While
End Using
Additionally, this still results in two separate trips to the database, where you go once to get the count and again to get the values. Not only is this very bad for performance, it leaves you open to a bug where another user changes your data from one query to the next.
There are several ways you can get this down to one trip to the database: loading the results into memory via a List or DataTable, changing the SQL to include the count and the values together, or adding a new column each time through the list. Here's an example using the last option:
DataGridView1.Columns.Clear()
Dim sql As String = "SELECT DISTINCT ServiceDate FROM tblattendance"
Using connection As New OleDbConnection("string here"), _
command As New OleDbCommand(sql, connection)
connection.Open()
Using reader As OleDbDataReader = command.ExecuteReader()
While reader.Read
Dim column As String = reader("ServiceDate").ToString()
DataGridView1.Columns.Add(column, column)
End While
End Using
End Using
Even better if you can use something like Sql Server's PIVOT keyword in combination with the DataGridView's AutoGenerateColumns feature for DataBinding, where you will write ONE SQL statement that has both column info and data, and simply bind the result set to the grid.
The For Next is incorrect. You execute your command for every column, when you only need to execute it once. The last result from the DataReader will be the header for every column as currently written.
You should iterate through your DataReader and increment the cursor variable there:
Dim i As Integer = 0
Using command = New OleDbCommand(sql, connection)
Using reader = command.ExecuteReader
While reader.Read
DataGridView1.Columns(i).HeaderText = reader("ServiceDate").ToString
i += 1
End While
End Using
End Using

Pass parameter to a query from another query in Access

I have a parameterized query GET_CUSTOMER:
SELECT * FROM Customer WHERE id = [customer_id]
I want to call this query from another query and pass it a parameter:
SELECT * FROM GET_CUSTOMER(123)
Note the above code is not valid, it is here to give you an idea of what I'm trying to do. Is it possible to do this in MS Access?
UPDATE 1:
The queries I posted are for example. The actual queries are much more complex. I know I can use table joins, but in my specific case it would be much easier if I could run parameterized queries inside other queries (that are parameterized as well). I can't use access forms because I'm using access with my .NET application.
This is how I end up solving this with help of https://stackoverflow.com/a/24677391/303463 . It turned out that Access shares parameters among all queries so there is no need to specifically pass parameters from one query to another.
Query1:
SELECT * FROM Customer WHERE ID > [param1] AND ID < [param2]
Query2:
SELECT * FROM Query1
VB.NET code:
Dim ConnString As String = "Provider=Microsoft.Jet.OleDb.4.0;Data Source=Database.mdb"
Dim SqlString As String = "Query2"
Using Conn As New OleDbConnection(ConnString)
Using Cmd As New OleDbCommand(SqlString, Conn)
Cmd.CommandType = CommandType.StoredProcedure
Cmd.Parameters.AddWithValue("param1", "1")
Cmd.Parameters.AddWithValue("param2", "3")
Conn.Open()
Using reader As OleDbDataReader = Cmd.ExecuteReader()
While reader.Read()
Console.WriteLine(reader("ID"))
End While
End Using
End Using
End Using
You can build the SQL on the fly.
MyID = prompt or get from user some ID
strSQl = "Select * from tblCustomer where ID in " & _
"(select * from tblTestCustomers where id = " & MyID
So you can nest, or use the source of one query to feed a list of ID to the second query.

Datagrid Duplication due to Select Statement VB

I am using an Access database, I believe the problem lies in my SQL statement. I have a relational database, with two tables -
StaffDetails [columns(
StaffID,
FirstName,
LastName)]
and
StaffTraining [columns(
StaffID,
Month)].
I have a combobox (cbMonth) and dependent on what month is chosen if the user selects 'January' then I would like the datagrid (DGTraining) to show the First Name and Last Name of the members of staff whose ID are within the chosen month. Sorry if this is not the clearest explanation, hopefully my code below makes my issue clearer:
Dim SqlQuery As String = "SELECT [StaffDetails.StaffID], [StaffDetails.FirstName], [StaffDetails.LastName], [StaffTraining.StaffID] FROM [StaffDetails], [StaffTraining] WHERE StaffTraining.TrainingMonth='" & cbMonth.Text & "'"
Dim da As OleDbDataAdapter = New OleDbDataAdapter(SqlQuery, conn)
Dim ds As DataSet = New DataSet
da.Fill(ds, "Training")
Dim dt As DataTable = ds.Tables("Training")
With DGTraining
.AutoGenerateColumns = True
.DataSource = ds
.DataMember = "Training"
End With
You are missing your join and are getting a cross join. 2 ways of addressing:
FROM [StaffDetails] inner join [StaffTraining] on [StaffDetails].staffID = [StaffTraining].staffID
That is the join logic thats more common and easier to read. You could add to your where clause (old method, harder to read and not as commonly accepted:
...where [StaffDetails].staffID = [StaffTraining].staffID and ...
Your last comment needs amending like this...
Dim SqlQuery As String = "SELECT [StaffDetails.StaffID], [StaffDetails.FirstName],
[StaffDetails.LastName], FROM [StaffDetails]
INNER JOIN [StaffTraining] ON [StaffDetails].StaffID = [StaffTraining].StaffID
WHERE [StaffTraining].TrainingMonth='" & cbMonth.Text & "'"
Also... dependant on how you have set up cbMonth you may want cbMonth.SelectedValue or cbMonth.SelectedText

Select Statement SQL in VB

I understand that there are other posts such as this, however I cannot find one that will work for me and Im really at the end of my tether with this, I really dont know what to do.
I have a few tables with ID columns and name columns, that are connected by Link Tables through foreign keys etc. I'm trying to enter data into the database via a GUI and to do so I'm using insert statements into the 'regular' tables, then Select statements to get the autogen IDs from the regular table to then insert into the link tables.
The code below is what I've been trying to use to do this.
Imports System.Data.SqlClient
Public Class Test
Private cs As New SqlConnection(".....")
Private Sub btnInsertNext_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnInsertNext.Click
Dim ContID As Integer
Dim FName As Integer
cs.Open()
Using command As New SqlCommand("Select FamID From Family Where Name = '" & FName & " '", cs)
command.Parameters.AddWithValue("#FamID", ContID)
command.ExecuteNonQuery()
End Using
Using command As New SqlCommand("Select DocID From Doctors Where DocName LIKE'" & AddFam.Doctor & " '", cs)
command.Parameters.AddWithValue("#DocID", AddFam.Doctor) ''AddFam is another form I'm using to add a family member to a Doctor
command.ExecuteNonQuery()
End Using
cs.close()
I'm using:
VB 2010
SQL server management 2008 r2
I understand its a bit muddley but any help would be greatly appreciated, and I'm sorry if this has come up before.
It's not really clear what problem you actually have, i assume that you don't know how to retrieve newly generated IDs.
Here is an self-explanatory example on how to retrieve new identity values with ADO.NET:
Using con = New SqlConnection(connectionString)
Dim newID As Int32
Using insertCommand = New SqlCommand("INSERT INTO Test(Value)VALUES(#Value);SELECT CAST(scope_identity() AS int)", con)
insertCommand.Parameters.AddWithValue("#Value", "Value1")
con.Open()
newID = DirectCast(insertCommand.ExecuteScalar, Int32)
End Using
If newID <> 0 Then
Using updateCommand = New SqlCommand("UPDATE TEST SET Value='Value1.1' WHERE idTest=#idTest", con)
updateCommand.Parameters.AddWithValue("#idTest", newID)
If con.State <> ConnectionState.Open Then con.Open()
Dim updatedRecordCount = updateCommand.ExecuteNonQuery
End Using
End If
End Using
The 2 important parts are:
SELECT CAST(scope_identity() AS int)
which will return the new identity value
DirectCast(insertCommand.ExecuteScalar, Int32)
which will return the new Identity column value if a new row was inserted, 0 on failure.
Since you're mixing using parameters with string-concatenation: using parameters is very important because it will prevent SQL-Injection.
SCOPE_IDENITY
ExceuteScalar-Method
Commands and Parameters in ADO.NET
NO! Parameterise your queries like this.
command = New SqlCommand("Select FamID From Family Where Name = #Fname", cs)
command.Parameters.AddWithValue("#Fname", ContID)
FamID = command.ExecuteScalar()
And ideally, you should use Scope_Identity() to get the Identity from you insert statement
You can try with this code on two queries based on # symbol
...
Using command As New SqlCommand("Select FamID From Family Where Name = #FamID", cs)
command.Parameters.AddWithValue("#FamID", ContID)
...
Using command As New SqlCommand("Select DocID From Doctors Where DocName LIKE #DocID", cs)
command.Parameters.AddWithValue("#DocID", AddFam.Doctor)
...