sql create string variable with column names - c#

i'm trying to create a dynamic sql statement either thru sql code or c# code. For example, I'd like to generate a result set based on a string containing a table name. Currently, I am doing this thru c# code.
My current issue is I'd like to generate a search similar to following
select * from customers
where ContactName+City like '%Berlin%'
so I'm thinking given a table name as a string parameter I need to somehow produce a string variable 'ContactName+City+etc' to build part of the search
I'm open to any other ideas as well.
var sql = string.Format(#"
select * from {0}
where {1} like '%criteria%'"
, variable_table
, "column1+column2+columnX"); //need function here to produce this string based on variable table?
Basically, how would I create a string that concatenates a variable number of columns together ('ContactName+City+etc') based on a variable_table?

Why not simply this:
select * from variable_table_name
WHERE column1+column2+columnX like '%criteria%'

You can do this purely in SQL as well. But as you have already done this in C# and you need only to get the list of columns based on the table name, try this.
Create a SQL udf as below.
CREATE FUNCTION funcReturnAllColumns
(
#tableName VARCHAR(50)
)
RETURNS VARCHAR(500)
BEGIN
DECLARE #ID INT
DECLARE #ALLColumns VARCHAR(500)
SET #ALLColumns = ''
SELECT #ID = id
FROM sys.sysobjects
WHERE name = #tableName
SELECT #ALLColumns = #ALLColumns + '+' + name
FROM sys.syscolumns
WHERE id = #ID
RETURN SUBSTRING(#ALLColumns,2,LEN(#ALLColumns))
END
SELECT dbo.funcReturnAllColumns('table_name')
OUTPUT:
Column1 + Column2 + ..... + ColumN
You may have to adjust varchar limits, validations as required.

Related

SQL Server Show Results when Record not found

I am trying to get SQL to list not found when a machine name lookup is not in the database. I have the query working but I need to integrate it into C# where I have other queries. As it stands it is a lot more convoluted than I want it to be, I am guessing there is a much more intelligent and concise why of having the SQL achieve the same result.
DECLARE #myHostCount as INT
DECLARE #myHostName1 as Varchar(50);
DECLARE #myHostName2 as Varchar(50) = 'Machine220054';
DECLARE #myHostCount1 as INT
DECLARE #myHostName3 as Varchar(50);
DECLARE #myHostName4 as Varchar(50) = 'Machine22054';
SET #myHostCount1 = (SELECT COUNT(*) FROM db_owner.host WHERE name LIKE '%Machine22054%')
SET #myHostName3 = (SELECT Name FROM db_owner.host WHERE name LIKE '%Machine22054%')
SET #myHostCount = (SELECT COUNT(*) FROM db_owner.host WHERE name LIKE '%Machine220054%')
SET #myHostName1 = (SELECT Name FROM db_owner.host WHERE name LIKE '%Machine220054%')
SELECT
CASE
WHEN #myHostCount = 1 THEN 'Found'
WHEN #myHostCount = 0 THEN 'Not Found'
END AS 'Result',
#myHostName2 AS 'HostName'
UNION
SELECT
CASE
WHEN #myHostCount1 = 1 THEN 'Found'
WHEN #myHostCount1 = 0 THEN 'Not Found'
END AS 'Result',
#myHostName4 AS 'HostName'
Output:
Result Machine Name
--------------------------
Found Machine220054
Not Found Machine22054
You can try this:
DECLARE #myHostName2 as Varchar(50) = 'Machine220054';
DECLARE #myHostName4 as Varchar(50) = 'Machine22054';
DECLARE #hostName TABLE (
Name VARCHAR(255)
);
INSERT INTO #hostName (Name)
VALUES (#myHostName2), (#myHostName4)
SELECT HostName = HostName.name,
Result = IIF(COUNT(*) = 0, 'Not Found', 'Found')
FROM #hostName AS HostName
LEFT JOIN db_owner.host ON host.name LIKE '%' + HostName.Name + '%'
GROUP BY HostName.name;
First: Try to remove as much procedural logic in SQL as possible.
Second: Try to minimize the use of "Like" queries if at all possible. It will hinder indexing and cause bottlenecks eventually, or, at least limit it to ending with a "%" instead of starting it with one.
Third: you are assuming that there will only be zero or one hit in your code. What if (given either non-unique or like based queries) there are more than one?
I'd go for something like this:
declare #looking table (name varchar(50))
insert into #looking (name) values ('Machine220054'),('Machine22054')
select case when t.name is null then 'Not Found' else 'Found' end as [FoundOrNot], t.name as [FoundName],l.name as [SearchName]
from #looking as l
left outer join db_owner.host as t
on t.name like '%' + l.name + '%'
UPDATE: Tested against a random table, removed "group by" on account of either one or no hit expected.
Just to provide an update, I ended up taking the results from the DR reader and entering them into a datatable, since the dr reader can only be read once. Then I compared the selections from the listbox with the contents from the datatable then added the difference to another datatable then displayed the results in a datagrid, this produced the desired outcome of results found, being listed and the objects not found being listed as not found.

Multiple table updates - avoiding multiple WHERE clauses

I looked around for a similar question (I'm sure there is one somewhere) but could not find one.
I have a list of IDs that for each of the IDs I need to update another column of that IDs row to the same string.
Essentially, I want something like this:
List<int> uniqueIDs;
UPDATE my_table
SET certainColumn = "foo bar"
WHERE uniqueID = uniqueIDs[0]
OR uniqueID = uniqueIDs[1]
...
OR uniqueID = uniqueID[uniqueIDs.Length-1]
I know this could be achieved by surrounding this in a for/foreach-loop, but I was wondering if there is a better way to get this done, possibly in one database connection?
Any help is greatly appreciated.
Well, you could use a TVP. First, create this on your server:
CREATE TYPE dbo.UniqueIDs AS TABLE(ID INT PRIMARY KEY);
Then a stored procedure:
CREATE PROCEDURE dbo.UpdateByID
#tvp dbo.UniqueIDs READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE t SET certainColumn = 'foo bar'
FROM dbo.my_table AS t
INNER JOIN #tvp AS tvp
ON t.uniqueID = tvp.ID;
END
Or:
CREATE PROCEDURE dbo.UpdateByID
#tvp dbo.UniqueIDs READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE t SET certainColumn = 'foo bar'
FROM dbo.my_table AS t
WHERE EXISTS (SELECT 1 FROM #tvp
WHERE ID = t.uniqueID);
END
Then instead of a List use a DataTable to hold your IDs in your C# application, and call the stored procedure, passing #tvp as a Structured parameter. I have simple examples of the C# side posted all over this site:
How to pass an array into a SQL Server stored procedure
Insert entire DataTable into database at once instead of row by row?
Parameters to the EXISTS clause in a stored procedure
var query = "UPDATE my_table
SET certainColumn = 'foo bar'
WHERE uniqueID in (" + String.Join(",", uniqueIDs) + ")"
I guess you could do something like this. Get your List of Ids in some TempTable or table variable and use IN operator in your update statement. something like this .....
UPDATE my_table
SET certainColumn = 'foo bar'
WHERE uniqueID IN (SELECT uniqueID
FROM #List_Table)

How to use a set of strings in a WHERE statement of SQL?

Sorry i am not sure how to titled the question well. I want to select few records in sql where a particular column is a set of strings.
Example . I have a table student and has columns ID and name. ID has records 1,2,3,4,5,6 . NAme has A,B,C,D,E,F.
I want to return C,D,E WHERE ID=[3,4,5].
I tried
SELECT FROM student WHERE ID=2,3,4
it gives error, ID=2,3,4 ='2,3,4' and it reads ID as a single columns. I am confused.
Also in my case, ID set are returned in a storedprocedure variable. that is like #ID
SELECT * FROM STUDENT WHERE ID=#ID
#ID above is a variable of a string type holding the set {1,2,3}. Please any help would be appreciated.
Try this:
SELECT * FROM student WHERE ID IN (2,3,4)
Syntax:
test_expression IN
( subquery | expression [ ,...n ]
)
Read more about IN operator here.
WHERE ID=2,3,4 and WHERE ID='2,3,4' are invalid syntax for SQL.
Looks like you can use IN (Transact-SQL) on your situation.
Determines whether a specified value matches any value in a subquery
or a list.
SELECT FROM student WHERE ID IN (2, 3, 4)
Also you might take a look Jeff's question Parameterize an SQL IN clause
If you are passing #ID as a variable with comma separated list of ids, WHERE IN (#ID) will not work.
I think best thing would be to use a Table Valued function to split them first and then query the table. Please check here for a Split() function.
Usage:
SELECT * FROM STUDENT
WHERE ID IN (
SELECT items FROM dbo.Split(#ID, ',') --Split function here
)
If you want to fitler multiple values in Select, you should use "in ()":
SELECT * FROM student WHERE ID in (2,3,4)
OR
SELECT * FROM student WHERE ID between 2 and 4
OR
SELECT * FROM student WHERE ID = 2 OR ID = 3 OR ID = 4
In this case take the first one.
The last one is very slow and not recommended in this scenario.
Please check this out
Select * from Student where Id IN ('2','3','4')
and check this out
Select Username from Student where ID IN ' + '('+ #Id +')'
where #Id=2,3,4
Select * from Student where Id='2'
union all
Select * from Student where Id='3'
union all
Select * from Student where Id='4'
Based on your comment below, you don't want to convert ID to an int. Instead, use LIKE to compare:
SELECT * from STUDENT
WHERE ', '+#ID+', ' LIKE ', '+CAST(ID as NVARCHAR(255)) + ', ';
However, the query will not be indexed. If you want the query to be indexed, then use dynamic SQL:
DECLARE #query NVARCHAR(max) = 'SELECT * FROM STUDENT WHERE ID IN ('+ #ID +')';
EXEC sp_executesql #query;
Since you are using Stored Procedure, that also has only equality compare i.e. id = 1, so either you have too execute three queries by splitting the input by comma separated values.
OR you can add a new procedure with a custom function to server with the SQL
CREATE FUNCTION dbo.myparameter_to_list (#parameter VARCHAR(500)) returns #myOutput TABLE (mytempVal VARCHAR(40))
AS
begin
DECLARE #TempTable table
(
mytempVal VARCHAR(40)
)
DECLARE #MySplittedValue varchar(40), #PositionOfComma int
SET #par = LTRIM(RTRIM(#parameter))+ ','
SET #PositionOfComma = CHARINDEX(',', #parameter, 1)
IF REPLACE(#parameter, ',', '') <> ''
BEGIN
WHILE #PositionOfComma > 0
BEGIN
SET #MySplittedValue = LTRIM(RTRIM(LEFT(#par, #PositionOfComma - 1)))
IF #MySplittedValue <> ''
BEGIN
INSERT INTO #TempTable (mytempVal) VALUES (#MySplittedValue) --Use conversion if needed
END
SET #par = RIGHT(#par, LEN(#par) - #PositionOfComma)
SET #PositionOfComma = CHARINDEX(',', #par, 1)
END
END
INSERT #myOutput
SELECT mytempVal
FROM #TempTable
RETURN
END
In your stored procedure you would use it like this:
Create Procedure StudentSelectFromSet
#Ids VARCHAR(MAX)
AS
SELECT * FROM student Stud
WHERE Stud.Id IN(SELECT value FROM dbo.myparameter_to_list (#Ids))
and then execute this new procedure as you were accessing earlier.

Update a table from two comma separated parameter as input

I have a Gridview in front end where Grid have two columns : ID and Order like this:
ID Order
1 1
2 2
3 3
4 4
Now user can update the order like in front end Gridview:
ID Order
1 2
2 4
3 1
4 3
Now if the user click the save button the ID and order data is being sent to Stored Procedure as #sID = (1,2,3,4) and #sOrder = (2,4,1,3)
Now if I want to update the order and make save I want to store it into database. Through Stored procedure how can update into the table so that the table is updated and while select it gives me the results like:
ID Order
1 2
2 4
3 1
4 3
There is no built in function to parse these comma separated string. However, yo can use the XML function in SQL Server to do this. Something like:
DECLARE #sID VARCHAR(100) = '1,2,3,4';
DECLARE #sOrder VARCHAR(10) = '2,4,1,3';
DECLARE #sIDASXml xml = CONVERT(xml,
'<root><s>' +
REPLACE(#sID, ',', '</s><s>') +
'</s></root>');
DECLARE #sOrderASXml xml = CONVERT(xml,
'<root><s>' +
REPLACE(#sOrder, ',', '</s><s>') +
'</s></root>');
;WITH ParsedIDs
AS
(
SELECT ID = T.c.value('.','varchar(20)'),
ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS RowNumber
FROM #sIDASXml.nodes('/root/s') T(c)
), ParsedOrders
AS
(
SELECT "Order" = T.c.value('.','varchar(20)'),
ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS RowNumber
FROM #sOrderASXml.nodes('/root/s') T(c)
)
UPDATE t
SET t."Order" = p."Order"
FROM #tableName AS t
INNER JOIN
(
SELECT i.ID, p."Order"
FROM ParsedOrders p
INNER JOIN ParsedIDs i ON p.RowNumber = i.RowNumber
) AS p ON t.ID = p.ID;
Live Demo
Then you can put this inside a stored procedure or whatever.
Note that: You didn't need to do all of this manually, it should be some way to make this gridview update the underlying data table automatically through data binding. You should search for something like this instead of all this pain.
You could use a table valued parameter to avoid sending delimiter-separated values or even XML to the database. To do this you need to:
Declare a parameter type in the database, like this:
CREATE TYPE UpdateOrderType TABLE (ID int, Order int)
After that you can define the procedure to use the parameter as
CREATE PROCEDURE UpdateOrder (#UpdateOrderValues UpdateOrderType readonly)
AS
BEGIN
UPDATE t
SET OrderID = tvp.Order
FROM <YourTable> t
INNER JOIN #UpdateOrderValues tvp ON t.ID=tvp.ID
END
As you can see, the SQL is trivial compared to parsing XML or delimited strings.
Use the parameter from C#:
using (SqlCommand command = connection.CreateCommand()) {
command.CommandText = "dbo.UpdateOrder";
command.CommandType = CommandType.StoredProcedure;
//create a table from your gridview data
DataTable paramValue = CreateDataTable(orderedData)
SqlParameter parameter = command.Parameters
.AddWithValue("#UpdateOrderValues", paramValue );
parameter.SqlDbType = SqlDbType.Structured;
parameter.TypeName = "dbo.UpdateOrderType";
command.ExecuteNonQuery();
}
where CreateDataTable is something like:
//assuming the source data has ID and Order properties
private static DataTable CreateDataTable(IEnumerable<OrderData> source) {
DataTable table = new DataTable();
table.Columns.Add("ID", typeof(int));
table.Columns.Add("Order", typeof(int));
foreach (OrderData data in source) {
table.Rows.Add(data.ID, data.Order);
}
return table;
}
(code lifted from this question)
As you can see this approach (specific to SQL-Server 2008 and up) makes it easier and more formal to pass in structured data as a parameter to a procedure. What's more, you're working with type safety all the way, so much of the parsing errors that tend to crop up in string/xml manipulation are not an issue.
You can use charindex like
DECLARE #id VARCHAR(MAX)
DECLARE #order VARCHAR(MAX)
SET #id='1,2,3,4,'
SET #order='2,4,1,3,'
WHILE CHARINDEX(',',#id) > 0
BEGIN
DECLARE #tmpid VARCHAR(50)
SET #tmpid=SUBSTRING(#id,1,(charindex(',',#id)-1))
DECLARE #tmporder VARCHAR(50)
SET #tmporder=SUBSTRING(#order,1,(charindex(',',#order)-1))
UPDATE dbo.Test SET
[Order]=#tmporder
WHERE ID=convert(int,#tmpid)
SET #id = SUBSTRING(#id,charindex(',',#id)+1,len(#id))
SET #order=SUBSTRING(#order,charindex(',',#order)+1,len(#order))
END

want to Search more then 2 keywords in ASP.net C#.net

i want to Search more then 2 item with one TEXTBOX field using with , Eg. item1,item2.
Result should be shown for both in one gridview i am using SP with one Parameter.
In the SP you will have to split the value.
Something like
--Split
DECLARE #textXML XML
DECLARE #data NVARCHAR(MAX),
#delimiter NVARCHAR(5)
SELECT #data = 'A,B,C',
#delimiter = ','
SELECT #textXML = CAST('<d>' + REPLACE(#data, #delimiter, '</d><d>') + '</d>' AS XML)
SELECT T.split.value('.', 'nvarchar(max)') AS data
FROM #textXML.nodes('/d') T(split)
Then You can use this in an WHERE IN claues or join to the SELECT as a SUB SELECT or CTE statement
in C#, split your values and pass it to SQL like #astander told,
you can use like,
var Values = Split(Textbox.text) //Textbox.Text.Tostring().Split("Delimeter")
now you can access array, like
Values[0]
Values[1]
Pass this to SQL query

Categories

Resources