[Form]: ![Form to be Considered][Form]
[Form]:
I have these form from Access. Here, I am developing search application on asp.net. I have 2 heavy databases with same data structure in which one database include approx 12000 fields and another has approx. 9000 records. I want to do search records with any criteria say,
Dealer Number = 3123 and DLicenceNo = 3242314
Here, I am assuming that if user provides a field text, that only considered to be search, and others to be ignored.
Is there anyway to build query for this rather using long if clauses?
Probably needs some tweaking but, start by naming all your textbox controls your column names in your database
var conditionals = new Dictionary<string, string>();
foreach(Control c in Page.Controls)
{
if (c is TextBox)
{
if (!String.IsNullOrEmpty(c.Text))
conditionals.Add(c.Id, c.Text);
}
}
From there you could be VERY careful to build a query that only has the right where clauses based on your conditionals dictionary. Ideally you can make sure it's parameterized some how to avoid all worries of SQL Injection.
I use stored procedures, passing parameters default values, like this:
select field1,field2,... from table1 where
(dealer_number= #dealer_number or #dealer_number='0')
and (d_licence_no=#d_licence_no or #d_licence_no='0')
If you're not using some parameter for this search, just send its default value, and that criteria will be ignored.
you can use Sql Case statement ,they are easy to manage ,
query can be like
declare #SearchItem varchar(50);
declare #SearchValue int;
SELECT column1, column2
FROM table
WHERE
#SearchValue =
CASE #SearchItem
WHEN 'Dealer Number' THEN ''
WHEN 'DLicenceNo ' THEN ''
END
use the stored procedure with all search criterias as your sp arguments and pass it null when you doesn't want to apply any condition. sp will be as
Create procedure usp_search
(
#dealerNumber int=null,
#licenseNumber int=null
)
as
select * from table where
dealerNumber= isnull(#dealerNumber,dealerNumber)
and licenseNumber = isnull(#licenseNumber ,licenseNumber )
Related
I would like to do the following but I lack the knowledge of Oracle SQL to do this.
I wish to give a userid as a string, and then a list of strings as a parameter to a procedure.
In the procedure I wish to make an insert into a table for each value in that list, together with the same user ID each time.
My questions:
I can't find an example of what to declare the input parameter as to make it a list. Do I need to make it a long varchar? I roughly know what the max length could be if the list is filled out entirely, but i was wondering if there is a data type for a sort of list?
How do I loop trough the list.
I see a lot of examples like this:
FOR r IN ('The', 'Quick', 'brown', 'fox')
LOOP
// Do stuff
END LOOP;
So does this mean I should provide my list as a single string with comma separate values? I was wondering if there are other ways to do this.
Here's a (PL/)SQL option you might want to consider.
For testing purposes, I've created a TEST table which will contain the ID - VALUE pairs. The procedure accepts two parameters, both are strings:
ID will be common for all values
VALUE is a comma-separated values list. Although you can pass a collection, I'd suggest you to use VARCHAR2 as it is quite simple to maintain
SELECT within the procedure uses a hierarchical query with regular expressions; its purpose is to split that comma-separated values string into rows so that you could insert each value into its own row. Doing so, you don't even need a loop. Besides, that SELECT would work even if you run it standalone, but - you want a procedure.
OK, here it goes:
SQL> create table test (id varchar2(10), value varchar2(20));
Table created.
SQL> create or replace procedure p_ins (par_id in varchar2,
2 par_value in varchar2)
3 is
4 begin
5 insert into test (id, value)
6 select par_id,
7 trim(regexp_substr(par_value, '[^,]+', 1, level))
8 from dual
9 connect by level <= regexp_count(par_value, ',') + 1;
10 end;
11 /
Procedure created.
Testing:
SQL> begin
2 p_ins('A', 'The, quick, brown fox, runs, or, whatever, it does');
3 end;
4 /
PL/SQL procedure successfully completed.
SQL> select * from test;
ID VALUE
---------- --------------------
A The
A quick
A brown fox
A runs
A or
A whatever
A it does
7 rows selected.
To execute the same insert N times with N different strings
string[] things = new[]{"foo", "bar", "baz");
SomeSqlCommand sql = new SomeSqlCommand("INSERT INTO table(a, b) VALUES(#a, #b)", "some connection string");
sql.Parameters.AddWithValue("#a", "fixed value");
sql.Parameters.AddWithValue("#b", "dummy value - will change in loop");
sql.Connection.Open();
foreach(string thing in things)
{
sql.Parameters["#b"].Value = thing;
sql.ExecuteNonQuery();
}
sql.Connection.Close();
Omitted using etc for clarity; basic premise: set up a parameterised SQL, set the parameter values, execute, change values, execute again...
When I used Oracle, parameter names were preceded by a colon; no idea if that's still true. Treat this as pseudocode (it's more like SQLServer syntax) and merge the concept into your existing Oracle style
Is it possible to create a key-value pair from an HTML radio button and text box value, then insert that pair into an array, then pass that array into a SQL Stored Procedure parameter that adds to the WHERE clause in the SP?
I am building a dashboard app that references one table in the SQL Server DB when it starts. This brings back the results of a simple select SP. I am pretty new to SQL so I am "psuedo-coding"(sorry)
DECLARE #SelectedDate NVARCHAR(50)
SELECT * FROM Tablename WHERE log_ts > #SelectedDate
However at this point I need to be able to select between 1-4 field names and give them a value to filter by.
I would love to set up those field names as a radio button, and to prevent injection, screen those inputs against known values in my application before sending them to the server, including inserting default values of the input is null.
My thought was to set up an array[string, string] once the screening is done, and pass that array into SQL as an array[field, value].(there would never be a null value).Here is what i would like the array to look like when being sent and all pairs are their default values:
QueryArray = ["application_name","Mobile"]
["login_id","*"]
["log_ts","Datetime.Today"]
["error_level","Exception"]
Then somehow pass it into the where clause like this:
SELECT *
FROM TableName
WHERE [application_name] = [Mobile]
AND [login_id]=[*]
AND [log_ts]=[Datetime.Today]
AND [error_level]=[Exception]
This is where my brain looks normally for a for-each or something, but SQL if pretty new to me... Any help would be greatly appreciated, even if its just a link to a previous article that I could not find...
It sounds like you want to use nullable parameters in conjunction with the coalesce operator. This works well if you have a known set of parameters, and then want to filter against them. So if you know all the possible fields you can search on, which is assuming you know ahead what fields you will display on your UI for people to filter with, then you can actually make a procedure like so:
create procedure [dbo].[MySampleProcedure]
#Property1Value <yourDataType> = null,
#Property2Value <yourDataType> = null,
....
as
begin
select
Col1,
Col2,
...
from
[dbo].[YourTable]
where
Column1Value = coalesce(#Property1Value, Column1Value)
and Column2Value = coalesce(#Property2Value, Column2Value)
....
end
This effectively means that if you omit certain parameters, you can still match based on the parameters you provide. When you receive your set from the UI, you can then match the pairs to parameters and you're off to the races.
If your field names are dynamic (not known ahead of time), then it becomes a more difficult issue. In that case, you will likely have to drop to dynamic SQL to accomplish what you want, and construct a query using parameterized SQL, to avoid injection attempts.
Currently, I have a stored procedure that returns all the columns (Select *). I am using this stored procedure to retrieve data from multiple tables, each table with different number of columns and obviously different column names, so Select * is convenient. However, now I need to retrieve only specific columns from each table so I want to pass the parameters something like this:
SELECT #param1, #param2, #param3, etc.
FROM #tableName
WHERE columnName = #tableId
The problem here is the number of parameters to be passed for the columns isn't set, as the table can have any number of columns. Is there a way to use some kind of loop or dynamic assignment so that I can pass any number of parameters as column names?
I know that I can filter out only the columns I want to use, and just leave out the rest, but that doesn't work in my case. I need the stored procedure to NOT return some specific columns with sensitive data.
I am using SQL Server 2008, ASP.NET MVC 4, and C# in my application.
If you are able to modify your stored procedure, you can easily put the required columns definitions as a parameter and use an auto-created temporary table:
CREATE PROCEDURE sp_GetDiffDataExample
#columnsStatement NVARCHAR(MAX) -- required columns statement (e.g. "field1, field2")
AS
BEGIN
DECLARE #query NVARCHAR(MAX)
SET #query = N'SELECT ' + #columnsStatement + N' INTO ##TempTable FROM dbo.TestTable'
EXEC sp_executeSql #query
SELECT * FROM ##TempTable
DROP TABLE ##TempTable
END
In this case you don't need to create a temp table manually - it is created automatically.
Hope this helps.
You can just pass one parameter that is a comma-delimited string of the columns you want to select and build a dynamic sql string.
#sql = 'SELECT ' + #param + ' FROM MyTable...';
EXECUTE (#sql);
If you use a dynamic sql solution, you should take care to guard against sql injection attacks.
You might also consider continuing to get all columns from the stored procedure, and showing only the columns the user wants in the front end.
Although I really do not like the stored procedure approach for this problem (I agree with #Gusman that building the query in C# is a better approach) you can make a stored procedure work without opening yourself to SQL Injection attacks. The following example is one simple way to do this:
Let's say that the table in question has the columns named COL1, COL2, and COL3. The stored procedure would accept a varchar(max) parameter named #IncludeCols and have code like:
SELECT CASE WHEN #IncludeCols LIKE '%#COL1#%' THEN COL1 ELSE '' END AS COL1,
CASE WHEN #IncludeCols LIKE '%#COL2#%' THEN COL2 ELSE '' END AS COL2,
CASE WHEN #IncludeCols LIKE '%#COL3#%' THEN COL3 ELSE '' END AS COL3
FROM <Table name>
WHERE <Where clause>
Yes, a column of every name will be returned, but data will only come from columns whose names are in the parameter. For example, if you wanted COL1 and COL3, the parameter value would be #COL1#COL3#. The # is important and must be on each side of every column name or any LIKE clause could get a false positive match.
If this is to export data to Excel then the best approach is likely to have a stored procedure that returns all of your columns minus the ones that should never be exported (passwords, other protected data, timestamps perhaps) and then filter out any additional unwanted columns in your front end.
You should never be using SELECT * for several reasons. Define what your application needs and program to that.
We are building an MVC project that needs to make use of of the MVC DataGrid. As part of that, we are wanting to allow for filtering and ordering of the DataGrid columns. We want this to be handled on the Sql side, with paging. Handling the paging is really straightforward and we've already got that working with our Stored Procedures.
The challenge we are facing now is how to get what columns the user has sorted by, into the stored procedure so we can sort the records during paging. I played with using a Table Type to send in a 'collection' of columns using something like this:
CREATE TYPE [dbo].[SortableEntity] AS TABLE(
[TableName] [varchar](50) NULL,
[ColumnName] [varchar](50) NULL,
[Descending] [bit] NULL
)
CREATE PROCEDURE [dbo].[DoSomethingWithEmployees]
#SortOrder AS dbo.SortableEntity READONLY
AS
BEGIN
SELECT [ColumnName] FROM #SortOrder
END
We're using Dapper as our ORM, and we're constrained to using only Stored Procedures by policy. In my Repository, I use the following DataTable to try and insert the records into the SortableEntity which works fine.
var parameters = new DynamicParameters();
// Check if we have anything to sort by
IEnumerable<SortDefinition> sortingDefinitions = builder.GetSortDefinitions();
if (sortingDefinitions.Count() > 0)
{
var dt = new DataTable();
dt.Columns.Add(nameof(SortableEntity.TableName));
dt.Columns.Add(nameof(SortableEntity.ColumnName));
dt.Columns.Add(nameof(SortableEntity.IsDescending));
Type tableType = typeof(SortableEntity);
foreach(SortDefinition sortDefinition in sortingDefinitions)
{
var dataRow = dt.NewRow();
dataRow.SetField(0, sortDefinition.TableName);
dataRow.SetField(0, sortDefinition.Column);
dataRow.SetField(2, sortDefinition.IsDescending);
dt.Rows.Add(dataRow);
}
parameters.Add("SortOrder", dt.AsTableValuedParameter(tableType.Name));
}
With this I'm able to get my sorted values into the stored procedure, but I'm concerned with Sql Injection. One way I can see getting around it is to lookup in the sys-columns table to see if the columns given are valid columns before using them. I'm not sure how to go about doing that, and taking the valid columns and applying them to an order by statement in my Stored Procedure. Since we're not using Sql parameter objects for the values being inserted into the DataTable, how do we protect against Sql injection? I know using DynamicParameters will protect us for the values going into the Stored Procedure parameters, but how does that work when the value is a table containing values?
The biggest challenge though is the WHERE clause. We want to pass in a filter from the data grid into the stored procedure, so users can filter out results sets. The idea being that the stored procedure would filter, order and page for us. I know I can handle this easily in Dapper using embedded or dynamic Sql; attempting to handle this via a Stored Procedure has proven to be over-my-head. What would I need to do to have my Stored Procedure receive a predicate from the app, applicable to a series of columns, that it applies as a WHERE clause in a safe manor, that won't open us up to Sql Injection?
I guess the only way to make your parameter inputs 'safe' is to check the values before assigning to your stored proc parameters. You'd have to look for 'SELECT', 'DELETE', and 'UPDATE'. But, I think since you are working with column names instead of entire dynamic SQL commands, you should be ok. Read the following: tsql - how to prevent SQL injection
But, I'm no expert on this. You should do your own research.
To give you an idea on how to handle dynamic filtering in a stored procedure, I just use a SQL function that splits up a string with comma separated values and turns it into a table. I JOIN this function with the table that contains the column that needs to be filtered. For example, I need to filter my dataset with multiple values using the DIVISION column from some table. My stored procedure will take in a optional VARCHAR parameter of length 3000:
#strDIVISION VARCHAR(3000) = NULL
Next, when receiving a NULL value for this parameter, give it an empty string value:
SELECT #strDIVISION = ISNULL(#strDIVISION,'')
Instead of filtering in the WHERE clause, you can JOIN the string split function as such:
...
FROM tblTransDTL td
INNER JOIN tblTransHDR th ON th.JOB_ID = td.JOB_ID
INNER JOIN dbo.udf_STRSPLIT(#strDIVISION) d1 ON
(d1.Value = th.DIVISION OR 1=CASE #DIVISION WHEN '' THEN 1 ELSE 0 END)
The CASE statement helps to determine when all values should be allowed or use only the values from the parameter input.
Lastly, this is the SQL function that splits the string values into a table:
CREATE FUNCTION udf_STRSPLIT
(
#Delim_Values VARCHAR(8000)
)
RETURNS #Result TABLE(Value VARCHAR(2000))
AS
begin
WITH StrCTE(start, stop) AS
(
SELECT 1, CHARINDEX(',' , #Delim_Values )
UNION ALL
SELECT stop + 1, CHARINDEX(',' ,#Delim_Values , stop + 1)
FROM StrCTE
WHERE stop > 0
)
insert into #Result
SELECT SUBSTRING(#Delim_Values , start, CASE WHEN stop > 0 THEN stop-start ELSE 4000 END) AS stringValue
FROM StrCTE
return
end
GO
How do I pass a list of names to a SQL parameter so I can do WHERE IN (#myList)?
I'm making an "advanced search" for our webpage where the user can list, separated by commas, things they would like to find for several inputs. For example there could be 5 input's (First Name, account number, Last name, ...) and for each of those inputs they can list several and separate them by commas.
My approach so far is to take in this string for each input, and format it so I can use the "IN" clause on that string. Is this possible?
IE the user for "First Name" enters "Bob, Joe,Fred", I would transform that input into
"'Bob', 'Joe', 'Fred'"
and then send that as a parameter to a stored procedure.
Declare #firstNames
Select * from Users
where User.FirstName in firstNames
OR should I put all these names into a DataTable in C# and pass that to SQL? If I should go this route, some examples would help a lot!
Thanks!
Whenever my query gets a little complicated like this, I prefer to choose either of these ways:
Create a query dynamically in C#, based on string concatenation for better readability rather than LINQ expression trees, and send it to SQL Server, or
Create a SP, or a Table-Valued Function in SQL Server, and then call it from C#, passing arguments to it.
In case of creating the query in C#, it's not extensible, and every time you want to change the logic, you should recompile your application.
In case of SP, because it's going to be a dynamic query and where clause should be created based on input parameters, I use exec (#query)
Assuming you are using SQL Server, you can create a tabular function like to split comma seperated values into table
CREATE FUNCTION dbo.Split(#String nvarchar(4000), #Delimiter char(1))
RETURNS #Results TABLE (Items nvarchar(4000))
AS
BEGIN
DECLARE #INDEX INT
DECLARE #SLICE nvarchar(4000)
-- HAVE TO SET TO 1 SO IT DOESNT EQUAL Z
-- ERO FIRST TIME IN LOOP
SELECT #INDEX = 1
WHILE #INDEX !=0
BEGIN
-- GET THE INDEX OF THE FIRST OCCURENCE OF THE SPLIT CHARACTER
SELECT #INDEX = CHARINDEX(#Delimiter,#STRING)
-- NOW PUSH EVERYTHING TO THE LEFT OF IT INTO THE SLICE VARIABLE
IF #INDEX !=0
SELECT #SLICE = LEFT(#STRING,#INDEX - 1)
ELSE
SELECT #SLICE = #STRING
-- PUT THE ITEM INTO THE RESULTS SET
INSERT INTO #Results(Items) VALUES(#SLICE)
-- CHOP THE ITEM REMOVED OFF THE MAIN STRING
SELECT #STRING = RIGHT(#STRING,LEN(#STRING) - #INDEX)
-- BREAK OUT IF WE ARE DONE
IF LEN(#STRING) = 0 BREAK
END
RETURN
END
Then you can call the function like this in your SP
Select * from Users
where User.FirstName in (
SELECT items FROM [dbo].[Split] (#firstNames, ','))
In C# you can add a list of parameters to your SQLCommand.CommandText. Assuming customerName is a string of "'Bob', 'Joe', 'Fred'" you do something like this:
Dim command As New SqlCommand(commandText, connection)
' Add FirstName parameter for WHERE clause.
command.Parameters.Add("#FirstName", SqlDbType.nvarchar)
command.Parameters("#FirstName").Value = FirstName
In Query Analyzer you can't have a list in a #parameter, which is a nuisance, but you can pass one in from another source; for example your C# calling code. In your WHERE clause you do WHERE IN (#Name). In my testing I create a temp table and do WHERE in (SELECT FirstName FROM #MyCustomerTempTable), then when hooking it up replace the sub-query with the singleton parameter.
Source: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.parameters(v=vs.110).aspx
Another approach (which I used most frequently) to adding parameters to the command is:
' Add the input parameter and set its properties.
Dim parameter As New SqlParameter()
parameter.ParameterName = "#FirstName"
parameter.SqlDbType = SqlDbType.NVarChar
parameter.Value = firstName
' Add the parameter to the Parameters collection.
command.Parameters.Add(parameter)
Source (scroll down to the example):
http://msdn.microsoft.com/en-us/library/yy6y35y8(v=vs.110).aspx
You can also dynamically build your query in C# or do a string.replace and replace #parameter with 'my list of names', but neither of these methods are preferred to adding parameter objects to the SQL Command object. I would suggest getting a solid understanding of the SQL Command object so you can build that instead of manipulating strings.