I am creating a web app in which i am using insert into select command
for which i have taken a stored procedure which looks like this
alter PROCEDURE profinalinstexpensesonid
(
#from varchar(5000),
#to varchar(5000),
#trainer varchar(5000),
#sonvinid varchar(5000),
#button varchar(5000),
#bill_id varchar(5000)
)
AS
BEGIN
if(#button='allselect')
begin
insert into listinvoice(sonvinid,tid,date,brandname,zone,location,area,venuename,venue,instructore,amount)
select
instructoreexpense.sonvinid,
sonvininsert.trainer,
convert(varchar,sonvininsert.date,105) as date ,
sonvininsert.brandname,
substring(sonvininsert.zone,1,1)as zone,
sonvininsert.location,
sonvininsert.area,
companysonvinunitvenue.venuename,
sonvininsert.venue,
sonvininsert.instructore,
instructoreexpense.amount
from
instructoreexpense
left outer join sonvininsert on
sonvininsert.sonvinid=instructoreexpense.sonvinid and
sonvininsert.status='0'
left outer join finalinstructoreexpense on
finalinstructoreexpense.sonvinid=instructoreexpense.sonvinid
left outer join companysonvinunitvenue on
companysonvinunitvenue.id=sonvininsert.comsonvinid
where
sonvininsert.sonvinid not in(select sonvinid from listinvoice)
and
sonvininsert.date
between convert(datetime,#from,105) and
convert(datetime,#to,105) and
sonvininsert.trainer=(select empname from trainerdetails where trid=#trainer)
and instructoreexpense.sonvinid NOT IN (
SELECT CAST(Item AS INTEGER)
FROM SplitString(#sonvinid, ',')
)
order by instructoreexpense.sonvinid
end
end
now this is working absolutely fine, now on this line
insert into listinvoice(sonvinid,tid,date,brandname,zone,location,area,venuename,venue,instructore,amount)
i want to add bill_id
and i will get my bill_id from a textbox in my program
so i want to change this line into something like this
insert into listinvoice(sonvinid,tid,date,brandname,zone,location,area,venuename,venue,instructore,amount,bill_id=#bill_id)
but i am gettig the error of
Msg 102, Level 15, State 1, Procedure profinalinstexpensesonid, Line
14 Incorrect syntax near '='.
what i need to do,
i am getting bill_id from my textbox,
is there any other options???
The problem is with your INSERT statement:
insert into listinvoice(sonvinid,tid,date,brandname,zone,
location,area,venuename,venue,instructore,amount,bill_id=#bill_id)
You haven't finished the statement. You would typically format it like this:
INSERT INTO (Col1, Col2, Col3) VALUES (#Val1, #Val2, #Val3)
You would first list the columns for which you want to provide values in the first group and then list the values themselves in the second group after the keyword VALUES).
In yours, you appear to be listing the columns you want to insert with the exception of the last one, where you're trying to also assign a value somehow. You need something like this:
insert into listinvoice(sonvinid,tid,date,brandname,zone,
location,area,venuename,venue,instructore,amount,bill_id)
VALUES (#sonvinid, ..., #bill_id)
Obviously you'd replace the ellipsis with your actual parameters.
You have to change your statement. You are currently doing this:
insert into listinvoice(sonvinid,tid,date,brandname,zone,location,area,venuename,venue,instructore,amount,bill_id=#bill_id)
Where it should be something like this:
insert into listinvoice(sonvinid,tid,date,brandname,zone,location,area,venuename,venue,instructore,amount,bill_id)
select
instructoreexpense.sonvinid,
sonvininsert.trainer,
convert(varchar,sonvininsert.date,105) as date ,
sonvininsert.brandname,
substring(sonvininsert.zone,1,1)as zone,
sonvininsert.location,
sonvininsert.area,
companysonvinunitvenue.venuename,
sonvininsert.venue,
sonvininsert.instructore,
instructoreexpense.amount,
#bill_id
from
instructoreexpense
left outer join sonvininsert on
sonvininsert.sonvinid=instructoreexpense.sonvinid and
sonvininsert.status='0'
left outer join finalinstructoreexpense on
finalinstructoreexpense.sonvinid=instructoreexpense.sonvinid
left outer join companysonvinunitvenue on
companysonvinunitvenue.id=sonvininsert.comsonvinid
where
sonvininsert.sonvinid not in(select sonvinid from listinvoice)
and
sonvininsert.date
between convert(datetime,#from,105) and
convert(datetime,#to,105) and
sonvininsert.trainer=(select empname from trainerdetails where trid=#trainer)
and instructoreexpense.sonvinid NOT IN (
SELECT CAST(Item AS INTEGER)
FROM SplitString(#sonvinid, ',')
)
order by instructoreexpense.sonvinid
Related
I want to let a user search through all the columns in a table for a set of phrases defined in a textbox (split terms with whitespace).
So what first came to mind is finding a way in SQL to concatenate all the columns and just use the LIKE operator (for each phrase) in this result.
The other solution I thought of is writing an algorithm which takes all the phrases searched, and match them with all the columns.
So I ended up with the following:
String [] columns = {"col1", "col2", "col3", "col4"};
String [] phrases = textBox.Text.Split(' ');
I then took all the possible combinations of columns and phrases, and put that into a where-clause-format for sql and then the result was
"(col1 LIKE '%prase1%' AND col1 LIKE '%phrase2%') OR
(col1 LIKE '%phrase1%' AND col2 LIKE '%phrase2%') OR
(col1 LIKE '%phrase2%' AND col2 LIKE '%phrase1%') OR
(col2 LIKE '%phrase1%' AND col3 LIKE '%phrase2%')"
The above is just an example snippet of the output, the amount of conditions being created in this algorith is measured by
conditions=columns^(phrases+1)
So I observed that having 2 search phrases can still give good performance, but more than that will certainly decrease performance drastically.
What is the best practise when searching all the columns for the same data?
Edwin,
I didn't know you was using ORACLE. My solution is using SQL Server. Hopefully you will get the gist of the solution and translate into PL/SQL.
Hopefully this is useful to you.
I am manually populating the #search temp table. You will need to somehow do that. Or look for some Split Function that will take the delimited string and return a Table.
IF OBJECT_ID('tempdb..#keywords') IS NOT NULL
DROP TABLE #keywords;
IF OBJECT_ID('tempdb..#search') IS NOT NULL
DROP TABLE #search;
DECLARE #search_count INT
-- Populate # search with all my search strings
SELECT *
INTO #search
FROM (
SELECT '%ST%' AS Search
UNION ALL
SELECT '%CL%'
) T1
SELECT #search_count = COUNT(*)
FROM #search;
PRINT #search_count
-- Populate my #keywords table with all column values from my table with table id and values
-- I just did a select id, value union with all fields
SELECT *
INTO #keywords
FROM (
SELECT client_id AS id
,First_name AS keyword
FROM [CLIENT]
UNION
SELECT client_id
,last_name
FROM [CLIENT]
) AS T1
-- see what is in there
SELECT *
FROM #search
SELECT *
FROM #keywords
-- I am doing a count(distinct #search.Search). This will get me a count,
--so if I put in 3 search values my count should equal 3 and that tells me all search strings have been found
SELECT #keywords.id
,COUNT(DISTINCT #search.Search)
FROM #keywords
INNER JOIN #search ON #keywords.keyword LIKE #search.Search
GROUP BY #keywords.id
HAVING COUNT(DISTINCT #search.Search) = #search_count
SELECT *
FROM [CLIENT]
WHERE [CLIENT].client_id IN (
SELECT #keywords.id
FROM #keywords
INNER JOIN #search ON #keywords.keyword LIKE #search.Search
GROUP BY #keywords.id
HAVING COUNT(DISTINCT #search.Search) = #search_count
)
You could create a stored procedure or function in PL/SQL to dynamically search the table for the search terms and then bring back the primary key and column of any matches. The code sample below should be enough to tailor to your requirements.
create table text_table(
col1 varchar2(32),
col2 varchar2(32),
col3 varchar2(32),
col4 varchar2(32),
col5 varchar2(32),
pk varchar2(32)
);
insert into text_table(col1, col2, col3, col4, col5, pk)
values ('the','quick','brown','fox','jumped', '1');
insert into text_table(col1, col2, col3, col4, col5, pk)
values ('over','the','lazy','dog','!', '2');
commit;
declare
rc sys_refcursor;
cursor_num number;
col_count number;
desc_tab dbms_sql.desc_tab;
vs_column_value varchar2(4000);
search_terms dbms_sql.varchar2a;
matching_cols dbms_sql.varchar2a;
empty dbms_sql.varchar2a;
key_value varchar2(32);
begin
--words to search for (i.e. from the text box)
search_terms(1) := 'fox';
search_terms(2) := 'box';
open rc for select * from text_table;
--Get the cursor number
cursor_num := dbms_sql.to_cursor_number(rc);
--Get the column definitions
dbms_sql.describe_columns(cursor_num, col_count, desc_tab);
--You must define the columns first
for i in 1..col_count loop
dbms_sql.define_column(cursor_num, i, vs_column_value, 4000);
end loop;
--loop through the rows
while ( dbms_sql.fetch_rows(cursor_num) > 0 ) loop
matching_cols := empty;
for i in 1 .. col_count loop --loop across the cols
--Get the column value
dbms_sql.column_value(cursor_num, i, vs_column_value);
--Get the value of the primary key based on the column name
if (desc_tab(i).col_name = 'PK') then
key_value := vs_column_value;
end if;
--Scan the search terms array for a match
for j in 1..search_terms.count loop
if (search_terms(j) like '%'||vs_column_value||'%') then
matching_cols(nvl(matching_cols.last,0) + 1) := desc_tab(i).col_name;
end if;
end loop;
end loop;
--Print the result matches
if matching_cols.last is not null then
for i in 1..matching_cols.last loop
dbms_output.put_line('Primary Key: '|| key_value||'. Matching Column: '||matching_cols(i));
end loop;
end if;
end loop;
end;
SELECT Col1, Col2, Col3, Col4
FROM Table1
WHERE User1 = #Owner
AND group1 = #Group
AND date1 BETWEEN #startDate AND #endDate
AND Mail LIKE #email
AND def IN (CASE #InvoiceMethod //Problem is Here
WHEN ''
THEN def
ELSE (#InvoiceMethod)
END)
A piece of code from the stored procedure. If am executing this, it's not returning any rows, even though it has some to return. Problem is with the IN clause, if I didn't pass anything to IN clause i.e #InvoiceMethod is null, then I'm getting rows.
If I pass anything to #InvoiceMethod, I'm not getting any rows.
The value in #InvoiceMethod is = 'A','B'
I tried many combinations like 'A','B' or "A","B" without any results.
How to pass values to IN clause please? In which format?
Please help me out of this.
Modified the stored procedure to the following,
Declare #tmpt table (value nvarchar(5) not null)
SET #InvoiceCount=(select COUNT(*) from dbo.fnSplit(#InvoiceMethod, ','))
SET #tempVar=1;
WHILE #tempVar<=(#InvoiceCount)
BEGIN
INSERT INTO #tmpt (value)
VALUES (#InvoiceMethod);//Here i need to insert array of values to temp table.like invoicemethod[0],invoicemethod[1]&invoicemethod[2] depends on #InvoiceCount
SET #tempVar=#tempVar+1;
END
--DECLARE #tmpt TABLE (value NVARCHAR(5) NOT NULL)
--INSERT INTO #tmpt (value) VALUES (#InvoiceMethod);
SELECT Col1,Col2,Col3,Col4
FROM Table1
WHERE User1 = #Owner
AND group1 = #Group
AND date1 between #startDate AND #endDate
AND Mail LIKE #email
AND def IN (SELECT value FROM #tmpt)
But not getting the results as expected :(
IMO this isn't a good way to approach this problem, by passing a list of filter values for a column in a comma separated string, as this is almost encouraging a Dynamic Sql approach to the problem (i.e. where you EXEC a built Sql string which pastes in the #InvoiceMethod as a string).
Instead, Sql 2008 has Table Valued Parameters, (and prior to this, you could use Xml), which allows you to pass structured data into a procedure in a table format.
You then just need to join to this table parameter to effect the 1..N valued IN () filtering.
CREATE TYPE ttInvoiceMethods AS TABLE
(
Method VARCHAR(20)
);
GO
CREATE PROCEDURE dbo.SomeProc
(
#InvoiceMethod ttInvoiceMethods READONLY, -- ... Other Params here
)
AS
begin
SELECT Col1, Col2, ...
FROM Table1
INNER JOIN #InvoiceMethod
ON Table1.def = #InvoiceMethod.Method -- Join here
WHERE User1 = #Owner
... Other Filters here
END
Have a look here for a similar solution with a fiddle.
Edit
The optional parameter (#InvoiceMethod = '') can be handled by changing the JOIN to the TVP with a subquery:
WHERE
-- ... Other filters
AND (Table1.def IN (SELECT Method FROM #InvoiceMethod))
OR #InvoiceMethod IS NULL)
To Initialize a TVP to NULL, just don't bind to it in C# at all.
I think a variable represetning multiple values with comma is not allowed in the in clause. You should either use string fiunctions (split and join) or go with the temp table solution. I prefer the second.
Use a temporary table to store your values and then pass it to your in statement
DECLARE #tmpt TABLE (value NVARCHAR(5) NOT NULL)
INSERT INTO #tmpt .........
...
...
SELECT Col1,Col2,Col3,Col4
FROM Table1
WHERE User1 = #Owner
AND group1 = #Group
AND date1 BETWEEN #startDate AND #endDate
AND Mail LIKE #email
AND def IN (SELECT value FROM #tmpt)
Used Splitfunctions to resolve the issue,Modified SQL Query
SELECT Col1, Col2, Col3, Col4
FROM Table1
WHERE User1 = #Owner
AND group1 = #Group
AND date1 BETWEEN #startDate AND #endDate
AND Mail LIKE #email
AND def IN (SELECT * FROM sptFunction(#InvoiceMethod,',')) //Problem is Here (Solved by using split functions)
I'm trying to write a shopping basket into a order + orderline in a sql database from C# asp.net.
the orderline will contain a ordernumber, total price, productid, quantity etc. for every item in the basket. The order itself will contain the ordernumber as primary key and will be linked to the different lines through it.
Everything worked fine yesterday, but now as i tried to use a SELECT command in the insert into statement to get things more dynamic i'm getting the above described syntax error.
Does anybody know what's wrong with this statement:
INSERT INTO [order]
(klant_id,totaalprijs,btw,subtotaal,verzendkosten)
SELECT klant.id
, SUM(orderregel.totaalprijs)
, SUM(orderregel.btw)
, SUM(orderregel.totaalprijs) - SUM(orderregel.btw)
, 7.50
FROM orderregel
INNER JOIN
klant
ON [order].klant_id = klant.id
WHERE klant.username = 'jerry'
GROUP BY
id;
the ordernumber in the "order" table is on autonumber, in the asp codebehind there is a for each which handles the lines being written for every product, there's an index set on 0 outside of this loop and is heightened with 1 every end of it. The executenonquery of the order is only executed once at the beginning of the first loop and the lines are added after with MAX(ordernumber) as ordernumber.
I hope i have provided enough information and somebody is capable of helping me.
Thanks in advance!
EDIT:
thanks everybody, using this query did it!
INSERT INTO [order]
(klant_id,totaalprijs,btw,subtotaal,verzendkosten) SELECT (SELECT klant.id FROM klant WHERE klant.username = 'jerry') ,
SUM(orderregel.totaalprijs) , SUM(orderregel.btw) ,
SUM(orderregel.totaalprijs) - SUM(orderregel.btw) , 7.50 FROM
orderregel;
You have used [order] in a JOIN, when it should be orderregel I guess.
FROM orderregel
INNER JOIN klant ON [order].klant_id = klant.id
should be:
FROM orderregel
INNER JOIN klant ON orderregel.klant_id = klant.id
Edit:
Why not just using:
INSERT INTO [order]
(klant_id,totaalprijs,btw,subtotaal,verzendkosten)
SELECT (SELECT klant.id FROM klant WHERE klant.username = 'jerry')
, SUM(orderregel.totaalprijs)
,...
... and avoid JOIN with klant table?
You can't refer to the table being inserted into. After all, those rows aren't yet there before the insert completes!
Reading your query, it's clear that you're trying to insert the klant called Jerry. But how do you specify which orderlines are used for the insert?
A possible solution:
Write the order first, with the klant id
Create the order lines. You know the orderid from the first query (f.e. using select SCOPE_IDENTITY()
Update the order with totals
Try this:
INSERT INTO [order]
(klant_id,totaalprijs,btw,subtotaal,verzendkosten)
SELECT klant.id
, SUM(orderregel.totaalprijs)
, SUM(orderregel.btw)
, SUM(orderregel.totaalprijs) - SUM(orderregel.btw)
, 7.50
FROM orderregel
INNER JOIN klant ON orderregel.id = klant.id
INNER JOIN [order] ON [order].klant_id = klant.id
WHERE klant.username = 'jerry'
GROUP BY id;
I was wondering if the below scenario will work? I am having trouble with it.
I have a smart tag SQLDataSource with a query like such:
SELECT [col1], [col2], [col3] FROM [Table1] WHERE (#SubType = #SubID) ORDER BY [col1] ASC
No matter where or how I set the #SubType parameter, it does not work, yet if I change the query to WHERE [col1] = #SubID (removing the #SubType) it works fine.
Can I set a parameter as a field name to compare against like my query does?
That's not how parameters work. Parameters are not string replacement. They work with values, not database objects names (Columns, Tables, etc.).
The solution is to first assemble the SQL query with the desired columns (code behind) and then set the parameter's values.
If you want to dynamically replace the items in your WHERE clause then you will want to look at using Dynamic SQL, then you can build your SQL as a string and execute it.
Code sample from http://www.sommarskog.se/dynamic_sql.html
DECLARE #sql nvarchar(2000)
SELECT #sql = 'SELECT O.OrderID, SUM(OD.UnitPrice * OD.Quantity)
FROM dbo.Orders O
JOIN dbo.[Order Details] OD ON O.OrderID = OD.OrderID
WHERE O.OrderDate BETWEEN #from AND #to
AND EXISTS (SELECT *
FROM dbo.[Order Details] OD2
WHERE O.OrderID = OD2.OrderID
AND OD2.ProductID = #prodid)
GROUP BY O.OrderID'
EXEC sp_executesql #sql, N'#from datetime, #to datetime, #prodid int',
'19980201', '19980228', 76
Another helpful link:
Dynamic WHERE Clause
The stored procedure:
ALTER PROC [Admin].[sp_Ques]
(
#QuesID bigint
)
AS
BEGIN
IF #QuesID = 0
SET #QuesID =NULL
SELECT FQ.QuesID, FQ.Ques,QuesAns
FROM Admin.Ques FQ
WHERE FQ.QuesID = Coalesce(#QuesID,QuesID)
SELECT Language FROM Admin.Language WHERE LanguageID=FQ.LanguageID
END
In the second Select statement:
SELECT Language FROM Admin.Language WHERE LanguageID=FQ.LanguageID
In this statement, I want the value of "FQ.LanguageID" from 1st select statement, so I wrote this:-
LanguageID=FQ.LanguageID
Apparently didn't work. It says "The multi-part identifier "FQ.LanguageID" could not be bound."
Do I need to pass this LanguageID to the stored procedure as a parameter and then use it as:-
SELECT Language FROM Admin.Language WHERE LanguageID=#LanguageID
How can I make this LanguageID=FQ.LanguageID work if I don't want to pass LanguageID as the second argument to the stored procedure? Is there a way?
Perhaps create a local variable to hold the LanguageID that's being retrieved. Assign a value to it during the previous SELECT. The addition of TOP 1 simply ensures that if/when you ever have multiple matches in the first query (indeed you will when #Ques is zero or null!), only one value is returned in that query, thereby allowing a single value into your variable.
DECLARE #Lang int --whatever datatype your QuesID is.
SELECT TOP 1
FQ.QuesID, FQ.Ques,QuesAns as QuesAns,
FQ.QuesAns[Answers], FQT.QuesType ,
FQ.QuesTypeID, FQ.QuesParentID, FQ.Active, FQ.AdminLanguageID
,#Lang = FQ.AdminLanguageID
FROM Admin.Ques FQ
LEFT OUTER JOIN Admin.QuesTypes FQT ON FQT.QuesTypeID=FQ.QuesTypeID
WHERE FQ.QuesID = Coalesce(#QuesID,QuesID)
SELECT TelerikLanguage FROM Admin.Language
WHERE AdminLanguageID=#Lang
The scope of FQ is limited to the first select statement.
Your options include:
Passing AdminLanguageID as a parameter as you have suggested
Retrieving AdminLanguageID in a prior statement (select #AdminLanguageID = AdminLanguageID from...)
Joining Admin.Language with Admin.Ques
Using a subquery (select ... from Admin.Language where AdminLanguageID in (select AdminLanguageID from Admin.Ques where ...)
Why not just join them into 1 select?
ALTER PROC [Admin].[sp_Ques]
(
#QuesID bigint
)
AS
BEGIN
IF #QuesID = 0
SET #QuesID =NULL
SELECT FQ.QuesID, FQ.Ques,QuesAns as QuesAns,FQ.QuesAns[Answers], FQT.QuesType ,FQ.QuesTypeID, FQ.QuesParentID, FQ.Active,FQ.AdminLanguageID, AL.TelerikLanguage
FROM Admin.Ques FQ
LEFT OUTER JOIN Admin.QuesTypes FQT ON FQT.QuesTypeID=FQ.QuesTypeID
LEFT JOIN Admin.Language AL ON AL.AdminLanguageID=FQ.AdminLanguageID
WHERE FQ.QuesID = QuesID OR #QuesID IS NULL
END