VB.net Search Query using two parameters - sql

I have created a search query in VB.net using datasource which has two parameters.
SELECT [Product Code], Description, Input_Date, Price, Unit, Quantity, Markup, Total
FROM Inventory_Table
WHERE (? = ?)
I made two parameters because I want to search by specific columns, this is how i used the query:
Inventory_TableTableAdapter.SearchQuery(DBDataSet1.Inventory_Table, InvSearchCombo.Text, InvSearchTxt.Text)
First parameter would be a dropdown combobox containing all columns from the table, the second parameter would be an input textbox.
But whenever i try searching nothing would appear.
What seems to be the problem? I really want to implement this kind of search feature. Thanks in advance.

In this you can use a dynamic code
Dim columnQuery As String = "Description"
Using command As New SqlCommand( "select Description,Input_Date from dep where " & columnQuery & " = #par1", connection)
command.Parameters.AddWithValue("#par1", "descripcion")
End using
EDIT
A better form could be this:
First, create store procedure:
CREATE PROCEDURE SP_LIST_TABLA_BY_DYNAMIC_COLUMN
#PAR_COLUMN VARCHAR(20),
#PAR_VALUE VARCHAR(20)
AS
DECLARE #STRSQL NVARCHAR(MAX)
SET #STRSQL = 'SELECT PRODUCT_CODE,DESCRIP,INPUT_DATE FROM INVENTORY_TABLE WHERE ' + #PAR_COLUMN + ' = ' + #PAR_VALUE
EXEC sp_executesql #STRSQL
Then invoke it:
Using command As New SqlCommand( "SP_LIST_TABLA_BY_DYNAMIC_COLUMN", connection)
command.CommandType = CommandType.StoredProcedure
command.Parameters.AddWithValue("#PAR_COLUMN", "product_code")
command.Parameters.AddWithValue("#PAR_VALUE", "1")
Using reader As SqlDataReader = command.ExecuteReader()
While reader.Read()
End While
End using
End using
But like user #Basic says: if the column name is coming from user input (even if via a database) then you're going to be vulnerable to SQL injection attacks
One suggestion could be evaluate that par_column name exists and par_value don't have some special characters.

Imagine you have a combo filter and save the ID as #par1 and text field save as #par2
Combo have this values:
none (id: -999)
field1 (id: 0)
field2 (id: 1)
Not sure about how you set your parameter so I will use some pseudo code.
You can make a trick to dynamic set what filter to use.
SELECT *
FROM Inventory_Table
WHERE
(Field1 = #par2 and 0 = #par1)
OR (Field2 = #par2 and 1 = #par1)
OR (-999 = #par1)
So if you select Field1 then 0 = #par1 will be true and first filter will be active
if you select Field2 then 1 = #par1 will be true and second filter will be active
if none is select all rows are return.

Related

How to use parameters in Visual Studio 2022 with an Access Database

I have created a database in Access 2019
I have created a basic form to display the data from the above table
I would like to filter the data to show only certain countries – like below
The where clause is hard code and so my question is how can I dynamically change the filter clause say from ‘Aus’ to ‘UK’.
a) I have tried using a parameter ‘CountryName’ as see in the Fill, GetData (CountryName), but I am unable to use the parameter in the Query Builder. How can this be done if possible?
b) Is there a way to change the Fill Query Property (CommandText) by code as I am unable to see the correct properties to use – see below
It sounds like you are creating a typed DataSet. In that case, just leave the default query as it is for each table adapter. You can then call Fill or GetData on a table adapter to get all the data in the corresponding table. If you want to be able to filter the data, add a new query with method names that reflect the filter, e.g. if you want to filter by the CountryName column then name the methods FillByCountryName and GetDataByCountryName. In the Query Builder, you have to use ? as parameter placeholders rather than names like #CountryName, e.g.
SELECT * FROM MyTable WHERE CountryName = ?
In code, you would then do something like this:
Dim countryName = "UK"
myTableAdapter.FillByCountryName(myDataSet.MyTable, countryName)
You can try this:
DECLARE #CountryName AS NVARCHAR(50) = 'uk'
SELECT CountryName FROM MyTable
WHERE CountryName = #CountryName
How this would translate to code:
Public Sub GetCountry()
DefaultCatalog = "MyTable"
Dim selectStatement = "SELECT CountyrName FROM MyTable WHERE CountyrName = #CountryName"
Using cn As New SqlConnection With {.ConnectionString = ConnectionString}
Using cmd As New SqlCommand With {.Connection = cn, .CommandText = selectStatement}
cmd.Parameters.AddWithValue("#CountryName", "uk")
cn.Open()
Dim reader = cmd.ExecuteReader()
If reader.HasRows Then
reader.Read()
Console.WriteLine(reader.GetString(0))
End If
End Using
End Using
End Sub

How to Safe input order by with SQLCommand in VbNet using SQLServer

Select cod,nom from tb_user where cod > #param0 order by #param1
Dim mycod = 3
Dim myorderby = "asc"
Dim _adapter = New SqlDataAdapter
cmd.CommandTimeout = timeout
cmd.Connection = _conn
cmd.CommandText = pSql
cmd.CommandType = CommandType.Text
Dim sqlParameter0 = New SqlParameter("#param0", mycod)
cmd.Parameters.Add(sqlParameter0)
Dim sqlParameter1 = New SqlParameter("#param1", myorderby)
cmd.Parameters.Add(sqlParameter1)
_adapter.SelectCommand = cmd
_adapter.Fill(_ds, "result")
I know I must replace the #param0 by the value of my variable mycod to be safe.
This is possible in the variables like the param0, but the #param1 where I put asc it gives me the following error:
the SELECT item identified by the ORDER BY number 1 contains a variable as part of the expression identitying a column position
PS: By the error it is clear the SqlParameter is not the way to input this kind of order by. Is there a way to input this kind of query safely?
You can do this by selectively ordering on the two columns.
Select cod,nom from tb_user
where cod > #param0
order by
case when #param1=1 then cod else 0 end,
case when #param1=2 then nom else 0 end
There are techniques to dynamically sort by a parameter but this can often lead to a significant slow down. I had a nightmare situation when I tried something similar.
The query worked well most of the time, but the query plan created was completely inappropriate when using a different parameter value, and the results took forever to return..
Since you're binding to a DataSet, you should just sort on the DefaultView after you call Fill().
_ds.Tables(0).DefaultView.Sort = myorderby

SqlDataReader read whole query

I have a SQL script with a loop. In each iteration I change the where clause. So I get several selects displayed. But my .net program reads only the first select. The SQL script works in ssms.
This is my SQL script
while (#aktuellParam <= #countParam)
Begin
SELECT name1,
name2
FROM dbo.tableName
WHERE name like #var
SET #aktuellParam = aktuellParam+1
END
This is my vb.net code
Using reader As SqlClient.SqlDataReader = _server.ConnectionContext.ExecuteReader(script)
Dim lfdnr As Integer = 1
Do While reader.Read()
spaltenListe.Add(New ISpalten With
{
.Name1= reader.GetString(reader.GetOrdinal("name1")),
.Name2= reader.GetString(reader.GetOrdinal("name2"))
})
lfdnr = lfdnr + 1
Loop
reader.Close()
End Using
That's because subsequent selects are actually in a different result set. Your reader needs to do a .NextResult after each read.

SQL Retrieving values from a statement with multiple selects

I have this SQL:
SELECT count (1) FROM users AS total_drafts WHERE version_replace = #sid
SELECT count (1) AS unpublished_drafts FROM users WHERE version_replace = #sid AND moderated = 0
SELECT * FROM users WHERE id = #sid ORDER By ID DESC
Which appears to be correct. However I'm having difficulty extracting the fields from the results. In vb.net I am using this code fragment:
While r.Read()
Dim o_email As String = CStr(r("email"))
Dim o_first_name As String = CStr(r("first_name"))
Dim o_last_name As String = CStr(r("last_name"))
Which is causing this error: System.IndexOutOfRangeException: email
I have checked the sql is being exucuted correctly. The sql I've posted is simply replacing a simpler statement which was feeding into the code fragment perfectly.
Why is this and how do I correct it?
the correct way:
While r.Read()
total_drafts = CInt(r("total_drafts"))
End While
r.NextResult()
While r.Read()
unpublished_drafts = CInt(r("unpublished_drafts"))
End While
error_status.Text = total_drafts & " " & unpublished_drafts
r.NextResult()
While r.Read()
Dim o_email As String = CStr(r("email"))
Dim o_first_name As String = CStr(r("first_name"))
Dim o_last_name As String = CStr(r("last_name"))
EDIT: r.NextResult() instead of r.ReadNext(), r.ReadNext() is for a DataTableReader
Assuming you are calling the whole sql statement in one go, the problem is that r.Read() will use the first datatable that is returned for the first statement(SELECT count (1) FROM users AS total_drafts WHERE version_replace = #sid ), which does not contain the email etc. fields.
You have to call r.NextResult() twice, this will move the datareader to the 3rd dataset that will contain the data from SELECT * FROM users WHERE id = #sid ORDER By ID DESC
You're returning three datasets. If "r" is a DataReader (unclear from your question) then you need to call;
r.NextResult
between your lines of code, like this;
While r.Read()
Dim o_email As String = CStr(r("email"))
r.NextResult()
Dim o_first_name As String = CStr(r("first_name"))
r.NextResult()
Dim o_last_name As String = CStr(r("last_name"))
One other possible explanation (again, unclear) is that you messed up your first column name ("email"), this would also give an out of range exception.
As far as I can understand you're trying to execute multiple statements, right?
You should separate your SQL statements with a semicolon and change the reader when you've finished with the previous one.
UPDATE:
I usually use stored procedures and return parameters for counters.
Something like this:
CREATE PROCEDURE usp_GetUsers (#sid INT, #unpublished INT OUTPUT)
AS
BEGIN
DECLARE #total_drafts INT
DECLARE #unpublished_drafts INT;
SELECT #total_drafts = count (1) FROM users WHERE version_replace = #sid
SELECT #unpublished_drafts = count (1) FROM users WHERE version_replace = #sid AND moderated = 0
SELECT * FROM users WHERE id = #sid ORDER By ID DESC
RETURN(#total_drafts)
END

Insert all values of a table into another table in SQL

I am trying to insert all values of one table into another. But the insert statement accepts values, but i would like it to accept a select * from the initial_Table. Is this possible?
The insert statement actually has a syntax for doing just that. It's a lot easier if you specify the column names rather than selecting "*" though:
INSERT INTO new_table (Foo, Bar, Fizz, Buzz)
SELECT Foo, Bar, Fizz, Buzz
FROM initial_table
-- optionally WHERE ...
I'd better clarify this because for some reason this post is getting a few down-votes.
The INSERT INTO ... SELECT FROM syntax is for when the table you're inserting into ("new_table" in my example above) already exists. As others have said, the SELECT ... INTO syntax is for when you want to create the new table as part of the command.
You didn't specify whether the new table needs to be created as part of the command, so INSERT INTO ... SELECT FROM should be fine if your destination table already exists.
Try this:
INSERT INTO newTable SELECT * FROM initial_Table
You can insert using a Sub-query as follows:
INSERT INTO new_table (columns....)
SELECT columns....
FROM initial_table where column=value
From here:
SELECT *
INTO new_table_name [IN externaldatabase]
FROM old_tablename
There is an easier way where you don't have to type any code (Ideal for Testing or One-time updates):
Step 1
Right click on table in the explorer and select "Edit top 100 rows";
Step 2
Then you can select the rows that you want (Ctrl + Click or Ctrl + A), and Right click and Copy
(Note: If you want to add a "where" condition, then Right Click on Grid -> Pane -> SQL
Now you can edit Query and add WHERE condition, then Right Click again -> Execute SQL, your required rows will be available to select on bottom)
Step 3
Follow Step 1 for the target table.
Step 4
Now go to the end of the grid and the last row will have an asterix (*) in first column (This row is to add new entry). Click on that to select that entire row and then PASTE (Ctrl + V). The cell might have a Red Asterix (indicating that it is not saved)
Step 5
Click on any other row to trigger the insert statement (the Red Asterix will disappear)
Note - 1: If the columns are not in the correct order as in Target table, you can always follow Step 2, and Select the Columns in the same order as in the Target table
Note - 2 - If you have Identity columns then execute SET IDENTITY_INSERT sometableWithIdentity ON and then follow above steps, and in the end execute SET IDENTITY_INSERT sometableWithIdentity OFF
If you are transferring a lot data permanently, i.e not populating a temp table, I would recommend using SQL Server Import/Export Data for table-to-table mappings.
Import/Export tool is usually better than straight SQL when you have type conversions and possible value truncation in your mapping. Generally, the more complex your mapping, the more productive you are using an ETL tool like Integration Services (SSIS) instead of direct SQL.
Import/Export tool is actually an SSIS wizard, and you can save your work as a dtsx package.
I think this statement might do what you want.
INSERT INTO newTableName (SELECT column1, column2, column3 FROM oldTable);
Dim ofd As New OpenFileDialog
ofd.Filter = "*.mdb|*.MDB"
ofd.FilterIndex = (2)
ofd.FileName = "bd1.mdb"
ofd.Title = "SELECCIONE LA BASE DE DATOS ORIGEN (bd1.mdb)"
ofd.ShowDialog()
Dim conexion1 = "Driver={Microsoft Access Driver (*.mdb)};DBQ=" + ofd.FileName
Dim conn As New OdbcConnection()
conn.ConnectionString = conexion1
conn.Open()
'EN ESTE CODIGO SOLO SE AGREGAN LOS DATOS'
Dim ofd2 As New OpenFileDialog
ofd2.Filter = "*.mdb|*.MDB"
ofd2.FilterIndex = (2)
ofd2.FileName = "bd1.mdb"
ofd2.Title = "SELECCIONE LA BASE DE DATOS DESTINO (bd1.mdb)"
ofd2.ShowDialog()
Dim conexion2 = "Driver={Microsoft Access Driver (*.mdb)};DBQ=" + ofd2.FileName
Dim conn2 As New OdbcConnection()
conn2.ConnectionString = conexion2
Dim cmd2 As New OdbcCommand
Dim CADENA2 As String
CADENA2 = "INSERT INTO EXISTENCIA IN '" + ofd2.FileName + "' SELECT * FROM EXISTENCIA IN '" + ofd.FileName + "'"
cmd2.CommandText = CADENA2
cmd2.Connection = conn2
conn2.Open()
Dim dA2 As New OdbcDataAdapter
dA2.SelectCommand = cmd2
Dim midataset2 As New DataSet
dA2.Fill(midataset2, "EXISTENCIA")