Where Is the Syntax Error in this SQL? - sql-server

I've got a query that is pretty much the same as many others which are used in the same library... but I did a lot of copy&paste on the SQL to add features to each one which are all similar but slightly different. Just below is the section which gives me the SQL Parser error. It fires at the Set rs = line.
dim sql, rs
sql = "DECLARE #str VARCHAR(255); SELECT #str = LOWER(?);" &_
"SELECT * ( " &_
"SELECT TOP 8 * FROM [oca_search_model] WHERE " &_
"[osm_isactive] = 1 AND " &_
"LOWER([osm_category]) = LOWER(?) AND " &_
"(LOWER([osm_keywords]) LIKE '%'+#str+'%' OR " &_
"LOWER([osm_description]) LIKE '%'+#str+'%' OR " &_
"LOWER([osm_name]) LIKE #str+'%') " &_
"ORDER BY [osm_weight] DESC" &_
") AS T1 ORDER BY [T1].[osm_category] ASC, [osm_weight] DESC, [osm_name] ASC;"
Set rs = executeQuery(conn, sql, Array(searchString, category))
The specific error I receive is: [Microsoft][ODBC SQL Server Driver][SQL Server]Incorrect syntax near the keyword 'ORDER'. I have printed out the SQL that is generated from that concatenation and it is as follows (with added line breaks):
DECLARE #str VARCHAR(255);
SELECT #str = LOWER(?);
SELECT * (
SELECT TOP 8 * FROM [oca_search_model]
WHERE [osm_isactive] = 1
AND LOWER([osm_category]) = LOWER(?)
AND (
LOWER([osm_keywords]) LIKE '%'+#str+'%'
OR LOWER([osm_description]) LIKE '%'+#str+'%'
OR LOWER([osm_name]) LIKE #str+'%'
)
ORDER BY [osm_weight] DESC
) AS T1
ORDER BY [T1].[osm_category] ASC, [osm_weight] DESC, [osm_name] ASC;
For your reference, I have added the executeQuery function below.
Function executeQuery(ByRef connection, ByRef querytext, ByRef parameters)
Dim cmd, i, rs
Set cmd = Server.CreateObject("ADODB.Command")
cmd.CommandText = querytext
cmd.CommandType = 1
cmd.Prepared = True
For i = 0 To UBound(parameters)
cmd.Parameters.Append(createVarCharInputParameter(cmd, "", parameters(i)))
Next
Set cmd.ActiveConnection = connection
Set rs = cmd.Execute()
Set executeQuery = rs
End Function
I don't have access to run the query directly on the server with some test values. But a similar query without the LOWER([osm_category]) = LOWER(?) AND portion runs just fine. Can you spot the syntax error in that SQL? My colleagues and I can't seem to.
Please note that it is imperative that I retain the ordering of the top 8 records by the osm_weight field. More specifically, the query needs to: get the top 8 by weight that match the category, and string (and are active). Then I need to order them by category so they're "grouped" and then within each category I need them ordered by weight and then by name.

You're missing the FROM after the first SELECT *.
DECLARE #str VARCHAR(255);
SELECT #str = LOWER(?);
SELECT * FROM (
SELECT TOP 8 * FROM [oca_search_model]
WHERE [osm_isactive] = 1
AND LOWER([osm_category]) = LOWER(?)
AND (
LOWER([osm_keywords]) LIKE '%'+#str+'%'
OR LOWER([osm_description]) LIKE '%'+#str+'%'
OR LOWER([osm_name]) LIKE #str+'%'
)
ORDER BY [osm_weight] DESC
) AS T1
ORDER BY [T1].[osm_category] ASC, [osm_weight] DESC, [osm_name] ASC;

The error is in this section:
AND (
LOWER([osm_keywords]) LIKE '%'+#str+'%'
OR LOWER([osm_description]) LIKE '%'+#str+'%'
OR LOWER([osm_name]) LIKE #str+'%'
) ORDER BY [osm_weight] DESC
) AS T1
The AND is part of the where query, and you can use parenthesis here, no problem. Bu the ORDER BY seems to add a set of mismatched Parenthesis.
This query should (syntactically at least) work:
DECLARE #str VARCHAR(255);
SELECT #str = LOWER(?);
SELECT TOP 8 * FROM [oca_search_model]
WHERE [osm_isactive] = 1
AND LOWER([osm_category]) = LOWER(?)
AND (
LOWER([osm_keywords]) LIKE '%'+#str+'%'
OR LOWER([osm_description]) LIKE '%'+#str+'%'
OR LOWER([osm_name]) LIKE #str+'%'
)
ORDER BY [osm_weight] DESC

Related

Returning values from a stored procedure ASP Classic [duplicate]

This question already has an answer here:
operation not allowed when the object is closed when running more advanced query
(1 answer)
Closed 1 year ago.
I am having a problem to output the results from a stored procedure using SQL Server and ASP Classic. If I have a simple SELECT into the procedure, it works fine. But with the code shown here, I get an error.
I have this stored procedure in SQL Server:
ALTER PROCEDURE [dbo].[Sp_Teste]
#data varchar(8)
AS
BEGIN
--DROP TABLE IF EXISTS #TempSubs
DECLARE #TempSubs TABLE
(
PedidoID Int,
NumeroPedido Varchar(20),
SubstituidoPor Varchar(8000)
)
INSERT INTO #TempSubs (PedidoID, NumeroPedido, SubstituidoPor)
SELECT
P.ID, P.NumeroPedido,
STRING_AGG(CAST(IPA.Quantidade AS varchar(5)) + 'X ' + Pd.Nome, ', ') + ' por ' + STRING_AGG(CAST(IPA.Quantidade AS varchar(5)) + 'X ' + Pd2.Nome, ', ') AS SubstituidoPor
FROM
Pedidos P, Clientes C, Produtos Pd, ItensPedidosAjustado IPA, Produtos Pd2
WHERE
P.ID = IPA.PedidoId
AND P.ClienteId = C.ID
AND Pd.ID = IPA.ProdutoId
AND Faltante = 1
AND CONVERT(Date, P.DataPedido, 103) = CONVERT(Date, #data, 103)
AND (IPA.ProdutoSubstituidoId <> 0)
AND Pd2.ID = IPA.ProdutoSubstituidoId
AND ((P.StatusPedido <> 'Pause' AND P.StatusPedido <> 'PULOU ENTREGA' AND P.StatusPedido <> 'Pedido Cancelado') OR P.StatusPedido IS NULL)
GROUP BY
P.ID, P.NumeroPedido, IPA.ProdutoSubstituidoId
SELECT
(SELECT STRING_AGG(Indisponibilidade, ', ')
FROM #TempIndis A
WHERE A.PedidoID = P.ID) AS Indisponibilidade,
(SELECT STRING_AGG(SubstituidoPor, ', ')
FROM #TempSubs A
WHERE A.PedidoID = P.ID) AS Substituicao
FROM
Pedidos P, Clientes C, ItensPedidosAjustado IPA
WHERE
P.ID = IPA.PedidoId
AND P.ClienteId = C.ID
AND Faltante = 1
AND CONVERT(Date, P.DataPedido, 103) = CONVERT(Date, #data, 103)
AND ((P.StatusPedido <> 'Pause' AND P.StatusPedido <> 'PULOU ENTREGA' AND P.StatusPedido <> 'Pedido Cancelado') OR P.StatusPedido IS NULL)
AND P.PedidoCancelado = 0
GROUP BY
P.ID, P.NumeroPedido, C.Nome, C.Email, P.TipoAssinatura
ORDER BY
numeropedido
END
and this code in ASP Classic
db_conn = "Provider=SQLNCLI11;Server=xxxx;Database=BaseGaia;Uid=sqlserver;Pwd=xxxxx;"
set conn = server.createobject("adodb.connection")
set Cmd = Server.CreateObject("ADODB.Command")
'-------------------------------------------------------
conn.open (db_conn)
'-------------------------------------------------------
set rs = Server.CreateObject("ADODB.RecordSet")
sSQL = "EXEC Sp_Teste #data = '20210301'"
set rs = conn.execute (sSQL)
response.write rs.eof
I get this error:
ADODB.Recordset error '800a0e78'
Operation is not allowed when the object is closed.
/Atendimento/testestoreprocedure.asp, line 18
By default, SQL Server returns DONE_IN_PROC (rowcount) messages after INSERT statements, which ADO classic returns a closed/empty result sets. These must be consumed by invoking the NextRecordset method before the SELECT query results are available.
To avoid the extra coding, make a habit of specifying SET NOCOUNT ON; in stored procedures to suppress those unwanted results. Add that to the beginning of the proc code to avoid the error.

SQL query crashed site or not responding from SQL to execute query

I have 5000 record into my database. Now I am going to update 500 record at a time but my site going to crash due to this execution and also SQL server not responding. Please help me to solve this issue.
public void UpdateProductStatus(int[] productIds)
{
if (productIds == null || productIds.Count() == 0)
return;
_dbContext.ExecuteSqlCommand(
" DECLARE #TempPIds AS TABLE(Id INT) " +
" INSERT INTO #TempPIds" +
" SELECT * FROM dbo.Split('" + string.Join(",", productIds) + "', ',') OPTION (MAXRECURSION 0) " +
" UPDATE Product SET IndexStatus = 1" +
" FROM Product p INNER JOIN #TempPIds tp ON p.Id = tp.Id WHERE IndexStatus = 0");
}
ALTER FUNCTION [dbo].[Split]
(
#String NVARCHAR(4000),
#Delimiter NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
WITH Split(stpos,endpos)
AS(
SELECT 0 AS stpos, CHARINDEX(#Delimiter,#String) AS endpos
UNION ALL
SELECT endpos+1, CHARINDEX(#Delimiter,#String,endpos+1)
FROM Split
WHERE endpos > 0
)
SELECT --'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
'Data' = SUBSTRING(#String,stpos,COALESCE(NULLIF(endpos,0),LEN(#String)+1)-stpos)
FROM Split
)
This query is proper but its slow down my site as well as SQL server. Please can anyone help me to speed up this query or alternative way to update my indexstatus?
put the ID string in an IN clause without split Could be wrong
Because a number can be contained within a number, if ID 12 is updated 12,1,2.
And for the TVP you can transfer to a temporary table and then you also gain performance

Best practice to pass a list as input in SQL query using vb.net [duplicate]

This question already has answers here:
Pass table valued parameter using ADO.NET
(5 answers)
Pass Array Parameter in SqlCommand
(11 answers)
Closed 5 years ago.
I am using vb.net with a SQL Server 2012 database.
I want to check the status of a list of files in the databases. The query is simple and something like this.
DECLARE #Filename NVARCHAR(100)
SET #Filename = 'MyFileName.doc'
SELECT DISTINCT
Docs.Filename,
Status.Name as 'Status'
FROM
[Documents] AS Docs
INNER JOIN
[Status] AS Status ON Status.StatusID = Docs.CurrentStatusID
WHERE
Docs.Filename LIKE #Filename
It works fine for one filename and I can launch it easily in vb.net using a sql connection as in this example.
Dim conn As New SqlConnection
If conn.State = ConnectionState.Closed Then
conn.ConnectionString = PDMConnectionString
End If
Try
conn.Open()
Dim sqlquery As String =
"DECLARE #Filename NVARCHAR(100)
SELECT DISTINCT
Docs.Filename,
Status.Name as 'Status'
FROM [Documents] AS Docs
INNER JOIN [Status] AS Status
ON Status.StatusID = Docs.CurrentStatusID
WHERE Docs.Filename LIKE #Filename "
Dim data As SqlDataReader
Dim adapter As New SqlDataAdapter
Dim parameter As New SqlParameter
Dim command As SqlCommand = New SqlCommand(sqlquery, conn)
With command.Parameters
.Add(New SqlParameter("#filename", "MyFileName.doc"))
End With
command.Connection = conn
adapter.SelectCommand = command
data = command.ExecuteReader()
While data.Read
'do something'
End While
Catch ex As Exception
End Try
The problem is that I need to find the status of a lot of files and I would like to do it with only one query.
I can do it directly in the query by by changing the last line like this, removing the parameter in vb.net and sending directly the query:
WHERE
Docs.Filename IN ('MyFileName.doc', 'MyOtherFileName.doc')
But it implies a lot of string concatenation and I don't really like how the code looks like with that solution.
What is the best practice to use in that type of situation in order to use less string concatenation and to make a code that is easier to manage?
You could use a function to take a comma separated string and return a table...
CREATE FUNCTION [dbo].[FileNames]( #FilenameValues nvarchar(max) )
RETURNS #Result TABLE( FileName nvarchar(max) )
AS
BEGIN
-- convert to an xml string
DECLARE #xml XML
SELECT #xml = CAST( '<A>' + REPLACE( #FilenameValues, ',', '</A><A>' ) + '</A>' AS XML )
-- select rows out of the xml string
INSERT INTO #Result
SELECT DISTINCT LTRIM( RTRIM( t.value( '.', 'nvarchar(max)' ) ) ) AS [FileName]
FROM #xml.nodes( '/A ') AS x(t)
RETURN
END
Then in your SQL either JOIN to it ...
JOIN (
SELECT * FROM dbo.FileNames( 'MyFileName.doc, MyOtherFileName.doc' )
) FileNames ON FileNames.FileName = Docs.Filename
OR use in a WHERE ...
WHERE Docs.Filename IN(
SELECT * FROM dbo.FileNames( 'MyFileName.doc, MyOtherFileName.doc' )
)

display data using stored procedure vb.net

if i click the search button, i keep on receiving an error at the value of IDNo, incorrect syntax near '11111' can someone help me?
With acc
IDNo = .IDNo
StartDate = DateTime.Parse(.StartDate).ToString("M/d/yyyy")
EndDate = DateTime.Parse(.EndDate).ToString("M/d/yyyy")
ProjectName = .ProjectName
ReferenceNo = .ReferenceNo
TaskCode = .TaskCode
FileName = .Filename
End With
dgAccomplishment.DataSource = Nothing
dgAccomplishmentPT.DataSource = Nothing
da = New SqlDataAdapter("dbo.process_time #User='" & IDNo & "' ,#From='" & StartDate & "',#To='" & EndDate & " 11:59:59 PM'", DB.GetConnection)
dt = New DataTable
da.Fill(dt)
dgAccomplishment.DataSource = dt
dgAccomplishment.Columns("ID").Visible = False
dgAccomplishment.Columns("TimeSave").Visible = False
da.Dispose()
dt.Dispose()
this is my stored procedure
SELECT a.ID, RTRIM(a.Last_User) [ID No.],
RTRIM(Users.FIRSTNAME + ' ' + Users.INITIAL + '. ' + Users.LASTNAME) [Name],
RTRIM(a.ProjectName) [Project Name],
a.ProjectNo, a.ProjectCode,
RTRIM(a.Filename) [Filename],
RTRIM(a.Filesize) [Filesize],
RTRIM(a.filesizeunit) [FileSizeUnit],
a.TimeSave [TimeSave]
from DBase.dbo.Acc a
INNER JOIN dbo.Users ON a.Last_User = Users.IDNo
WHERE a.Last_User in (#user)
and CONVERT(VARCHAR(10),timesave,101) BETWEEN #From AND #To
ORDER BY RTRIM(a.SubGroup), RTRIM(a.Last_User)
but when i try to run the procedure in a query it works well.
Because you are using string concatenation, you have the age old single quote problem: If IDNo value contains a single quote, then your query will fail.
What's worse, your code is susceptible to sql injection attacks.
You have to escape ALL parameters for single quotes, replacing them by 2 single quotes.
Best solution here: use parametrized sql

vb table adapter does not allow more than one parameter in the IN clause

What I need to achieve is to send a list of unknown QTY of values to a Sql server NOT IN clause but can only achieve this with singular values. below is my Sql statement:
SELECT SorMaster.LastInvoice
, SorMaster.SalesOrder
, SorMaster.OrderStatus
, ArCustomer.RouteCode
, SorMaster.Customer
, SorMaster.CustomerName
, SorMaster.CustomerPoNumber
, SorMaster.OrderDate
, SorMaster.DateLastInvPrt
, ArInvoice.InvoiceBal1
, ArInvoice.TermsCode
FROM SorMaster AS SorMaster
INNER JOIN ArCustomer AS ArCustomer ON SorMaster.Customer = ArCustomer.Customer
INNER JOIN ArInvoice AS ArInvoice ON SorMaster.LastInvoice = ArInvoice.Invoice
WHERE (SorMaster.OrderStatus = '9')
AND (SorMaster.Branch LIKE 'J%')
AND (SorMaster.DocumentType = 'O')
AND (SorMaster.LastInvoice > #Last_Invoice)
AND (SorMaster.OrderDate > DATEADD(Month, - 4, GETDATE()))
AND (SorMaster.LastInvoice NOT IN (#ExclusionList))
ORDER BY SorMaster.LastInvoice
The #ExclusionList value is generated by this code as a string from a listbox:
Dim exclusion As String = ""
If MenuForm.ExclusionCB.Checked = True Then
For i = 0 To MenuForm.ExclusionLB.Items.Count - 2
exclusion = exclusion & MenuForm.ExclusionLB.Items(i) & ","
Next
exclusion = exclusion & MenuForm.ExclusionLB.Items(MenuForm.ExclusionLB.Items.Count - 1)
Else
exclusion = ""
End If
I have also tried sending the entire listbox as a collection.
Does anyone know how I can send more than one value (something like 1,2,3,4,5,6) and have sql understand that these is more than one? I won't have an issue with the SELECT statement changing, just as long as it returns the same information.
The reason I need this with the exception list, is our remote DB PK is on the Salesorder column and the local DB is on the LastInvoice column
Hope this makes sense. if you need more info, please let me know
You can send it as a string and use dynamic sql. Here's a simple example how to do that.
DECLARE #vals VARCHAR(50) = '1,2,3,4,5,6'
DECLARE #sql VARCHAR(MAX) = 'SELECT * FROM TABLE WHERE FIELD1 IN'
SET #sql = #sql + ' (' + #vals + ')'
-- #sql = 'SELECT * FROM TABLE WHERE FIELD1 IN (1,2,3,4,5,6)'
EXEC (#sql)

Resources