sp_send_dbmail Error Formatting Query - sql-server

I have a SQL agent job that was working, but after adding a new left join to the dbmail query it's failing. The error is one I've seen all over that few people seem to have an answer for:
Msg 22050, Level 16, State 1, Line 0
Error formatting query, probably invalid parameters
Msg 14661, Level 16, State 1, Procedure sp_send_dbmail, Line 517
Query execution failed: Msg 105, Level 15, State 1, Server SQLSRV, Line 34
The message after Query Execution Failed tends to change and pick locations in the middle of words as the false culprit.
The query is as follows:
#Query = 'set nocount on
USE [Epicor10]
select
case when VoidOrder = 0 then "Not Void" else "Voided" end as ' + #Column1Name + ',
case when Approve = 0 then "NA" else "A" end as Approved,
case when poheader.Confirmed = 0 then "UC" else "C" end as Confirmed,
POHeader.PONum,
podetail.POLine,
pr.PORelNum,
TG.GLAccount,
convert(nvarchar,REPLACE(REPLACE(REPLACE(REPLACE( podetail.PartNum, CHAR(13), ""), CHAR(10), " "), CHAR(9), " "), ",", " ")) PartNum,
convert(nvarchar,REPLACE(REPLACE(REPLACE(REPLACE( podetail.LineDesc, CHAR(13), ""), CHAR(10), " "), CHAR(9), " "), ",", " ")) LineDesc,
OrderQty,
ISNULL(CONVERT(nVarChar(max), pr.DueDate, 121), NULL) RelDueDate,
ISNULL(CONVERT(nVarChar(max), PODetail.DueDate, 121), NULL) LineDueDate,
pr.ReceivedQty,
pr.RelQty,
(pr.RelQty - pr.ReceivedQty) BalanceDue,
EntryPerson,
ShipName,
BuyerID,
POHeader.VendorNum,
Vendor.VendorID,
convert(nvarchar(max),REPLACE(REPLACE(REPLACE(REPLACE(Vendor.Name, CHAR(13), ""), CHAR(10), " "), CHAR(9), " "), ",", "")) Name,
REPLACE(REPLACE(REPLACE(REPLACE(ISNULL(CONVERT(nVarChar(max),ApprovedDate, 121), NULL), CHAR(13), ""), CHAR(10), " "), CHAR(9), " "), ",", "") LineApprovedDate,
ISNULL(CONVERT(nVarChar(max),OrderDate, 121), NULL) OrderDate,
convert(nvarchar,REPLACE(REPLACE(REPLACE(case when ApprovedBy = "username" then "usersname" else ApprovedBy End, CHAR(13), ""), CHAR(10), " "), CHAR(9), " ")) ApprovedBy
from [Epicor10].[Erp].[POHeader]
inner join [Epicor10].[Erp].Vendor on POHeader.VendorNum = Vendor.VendorNum
left join [Epicor10].[Erp].PODetail on poheader.company = podetail.company and poheader.PONum = podetail.PONUM
left join [Epicor10].[Erp].PORel pr on PODetail.Company = pr.company and PODetail.ponum = pr.PONum and PODetail.poline = pr.POLine
left join [Epicor10].[Erp].TranGLC TG on TG.Key1 = pr.PONum and TG.Key2 = pr.POLine and TG.Key3 = pr.PORelNum and TG.RelatedToFile = "PORel"
where POHeader.OpenOrder = 1 and OrderDate >= "2016-7-1" and OpenLine = 1 and VoidLine = 0 and VoidRelease = 0 and OpenRelease = 1
order by PONum, POLine, PORelNum'
Now this was working and sending fine until I was asked to add the GLAccount column, the last 'Left Join' on TranGLC is the trigger for the issue. If I remove that join again, it works, but I can't figure out what the problem with that is.
I've checked permissions on the agent (though I can't imagine why that table would've made a difference), I've done many redundant things to make sure its pointing to the database and schema. I've copied the query and run it standalone in another window and it works fine. Nothing I've found online has been helpful in pinpointing the problem.
UPDATE FOR COMMENTS:
Can I send it without the query?: Yes, I can even send the email with the query until I add the left join to TranGLC.
Permissions issue?: I also saw that a lot online as the usual cause, and that would make sense here. It doesn't seem to be the case though. No special permissions on this table that I can see. Its setup like the rest of the tables in the database>schema
Job Agent User: So here is where I'm a bit confused on the setup/relationship of everything. I'm running the sp under a Profile, which is set up under Management>Database Mail using Basic Authentication with an email we have for our domain. This email is actually the email for a domain user we have. I could try setting it to Windows Auth for the active user (me, since I'm an admin).
What I'm unclear about is how that works within SSAgent, most of what I've seen says to set up database mail under SSAgent>Properties>Alert System (Enable Mail profile is NOT checked here for us) but I'm unclear about the relationship. I feel like that's for something else entirely and shouldn't matter here.

From the looks online, everything points to some sort of permission issue somewhere in the process.
Questions to check would be: Are you able to send the email manually i.e. run the same exact job manually, not the query? Does that table or column have any special permissions? What happens if you change the user that the job agent runs as?

Related

wrong result in Apache flink full outer join

I have 2 data streams which were created from 2 tables like:
Table orderRes1 = ste.sqlQuery(
"SELECT orderId, userId, SUM(bidPrice) as q FROM " + tble +
" Group by orderId, userId");
Table orderRes2 = ste.sqlQuery(
"SELECT orderId, userId, SUM(askPrice) as q FROM " + tble +
" Group by orderId, userId");
DataStream<Tuple2<Boolean, Row>> ds1 = ste.toRetractStream(orderRes1 , Row.class).
filter(order-> order.f0);
DataStream<Tuple2<Boolean, Row>> ds2 = ste.toRetractStream(orderRes2 , Row.class).
filter(order-> order.f0);
I wonder to perform a full outer join on these 2 streams, and I used both orderRes1.fullOuterJoin(orderRes2 ,$(exp))
and a sql query containing a full outer join, as below:
Table bidOrdr = ste.fromDataStream(bidTuple, $("orderId"),
$("userId"), $("price"));
Table askOrdr = ste.fromDataStream(askTuple, $("orderId"),
$("userId"), $("price"));
Table result = ste.sqlQuery(
"SELECT COALESCE(bidTbl.orderId,askTbl.orderId) , " +
" COALESCE(bidTbl.userId,askTbl.orderId)," +
" COALESCE(bidTbl.bidTotalPrice,0) as bidTotalPrice, " +
" COALESCE(askTbl.askTotalPrice,0) as askTotalPrice, " +
" FROM " +
" (SELECT orderId, userId," +
" SUM(price) AS bidTotalPrice " +
" FROM " + bidOrdr +
" Group by orderId, userId) bidTbl full outer JOIN " +
" (SELECT orderId, userId," +
" SUM(price) AS askTotalPrice" +
" FROM " + askOrdr +
" Group by orderId, userId) askTbl " +
" ON (bidTbl.orderId = askTbl.orderId" +
" AND bidTbl.userId= askTbl.userId) ") ;
DataStream<Tuple2<Boolean, Row>> = ste.toRetractStream(result, Row.class).filter(order -> order.f0);
However, the result in some cases in not correct: imagine user A sells with a price to B 3 times, after that user B sells to A 2 times, the second time the result is:
7> (true,123,a,300.0,0.0)
7> (true,123,a,300.0,200.0)
10> (true,123,b,0.0,300.0)
10> (true,123,b,200.0,300.0)
the second and forth lines are the expected result of stream, but it will generate the 1st and 3rd lines too.
worth mentioning that coGroup is the other solution, yet I do not want to use windowing in this scenario, and a non-windowing solution is just accessible in bounded streams (DataSet).
Hint: orderId and userId will repeat in both streams, and I want to produce 2 rows in each action, containing:
orderId, userId1, bidTotalPrice, askTotalPrice AND
orderId, userId2, bidTotalPrice, askTotalPrice
Something like this is to be expected with streaming queries (or in other words, with queries executed on dynamic tables). Unlike a traditional database, where the input relations to a query are kept static during query execution, the inputs to a streaming query are being continuously updated -- and so the result must also be continuously updated.
If I understand the setup here, the "incorrect" results on lines 1 and 3 are correct up until the relevant rows from orderRes2 are processed. If those rows never arrive, then lines 1 and 3 will remain correct.
What you should expect is an eventually correct result, including retractions as necessary. You can reduce the number of intermediate results by turning on mini-batch aggregation.
This mailing list thread gives more insight. If I've misunderstood your situation, please provide a reproducible example that illustrates the problem.

How to update multiple records in SQL using Classic ASP

I'm trying to update a two records with the same social in Classic ASP but I'm not sure how. This is what I have... but it don't update both records.
set rsTblEmpl=Server.CreateObject("ADODB.Recordset")
if 1=1 then
sql = "select * from tblEmpl where ssn=" & strSsn & ";"
else
sql = "select * from tblEmpl where eight_id=" & intEight_id & ";"
end if
rsTblEmpl.open sql, conn, 2, 3
if not rsTblEmpl.eof then
rsTblEmpl("posid") = StrPosId
rsTblEmpl("posname") = StrPosName
rsTblEmpl.update
end if
rsTblEmpl.close
set rsTblEmpl=nothing
The reason why it's only updating 1 value is because you're not looping over all records. You're only going to be updating the first record that it finds. So if there are multiple records with the same SSN you will have to change the if statement to a loop.
do until rsTblEmpl.eof
rsTblEmpl("posid") = StrPosId
rsTblEmpl("posname") = StrPosName
rsTblEmpl.update
rsTblEmpl.movenext
loop
Or you can do them all in one go using a parameterized UPDATE statement:
UPDATE tblEmpl SET posid = ?, posname = ? WHERE ssn = ?
The if 1=1 at the top I'll assume is a placeholder, because now it will always be true, so the else is not needed.

Boolean expressions in SQL Server jobs

I have inherited a SQL Server "job" that does several things. There are two "steps" and each has multiple statements one is:
UPDATE Person
SET Person.LastName = P.LastName,
Person.FirstName = P.FirstName,
Person.MiddleName = P.MiddleName,
Person.EmailAddress = P.EmailAddress,
Person.StartDate = P.StartDate,
Person.EndDate = P.EndDate
FROM OtherDB.dbo.Person Person
INNER JOIN FirstDB.dbo.Persons AS P ON P.PersonId = Person.PersonId
WHERE Person.LastName != P.LastName
OR Person.FirstName != P.FirstName
OR Person.MiddleName != P.MiddleName
OR Person.EmailAddress != P.EmailAddress
OR Person.StartDate != P.StartDate
OR Person.EndDate != P.EndDate;
It is updating "person" data from FirstDB into OtherDB. The PersonId columns are bigints and are not null. The various "date" columns are of type datetime and could be NULL. The other columns are all varchar and could be NULL.
What I have learned is that, in the where clause, if NULL appears on either or both sides of the boolean operator the result is undefined. Basically, NULL can neither equal nor not-equal NULL. It appears that the same applies to NULL and any other non-null value.
So I thought to try:
UPDATE Person
SET Person.LastName = P.LastName,
Person.FirstName = P.FirstName,
Person.MiddleName = P.MiddleName,
Person.EmailAddress = P.EmailAddress,
Person.StartDate = P.StartDate,
Person.EndDate = P.EndDate
FROM OtherDB.dbo.Person Person
INNER JOIN FirstDB.dbo.Persons AS P ON P.PersonId = Person.PersonId
WHERE ISNULL(Person.LastName, '') != ISNULL(P.LastName, '')
OR ISNULL(Person.FirstName, '') != ISNULL(P.FirstName, '')
OR ISNULL(Person.MiddleName, '') != ISNULL(P.MiddleName, '')
OR ISNULL(Person.EmailAddress, '') != ISNULL(P.EmailAddress, '')
OR ISNULL(Person.StartDate, '') != ISNULL(P.StartDate, '')
OR ISNULL(Person.EndDate, '') != ISNULL(P.EndDate, '');
This works in a regular query window but fails in the job. The error is:
An expression of non-boolean type specified in a context where a condition is expected, near 'OR'. [SQLSTATE 42000] (Error 4145). The step failed.
I am not seeing a problem. What am I missing?
Edit
As requested, for others in this situation: I edited this job by using SQL Server Management Studio. I opened a connection to my target DB then opened the "SQL Server Agent" drop-down under the connection. I opened "Jobs" and found the job i was looking for. I right-clicked on the job name and selected "Script Job As" -> "Drop and Create to" -> "New Query Window". From there I copied the relevant sections to new query windows where I modified and tested them as necessary. I then just copied and pasted the working sections back into the job window above -- COMPLETELY forgetting to double up the single quotes where necessary.
How did you add the step to the job? I wonder if a script you used escaped all of your double apostrophes and it is now trying to evaluate
WHERE ISNULL(Person.LastName, ') != ISNULL(P.LastName, ')
------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^
That's "valid enough" syntax. The highlighted portion is a simple string literal, not an empty string and a comparison to another expression.

SQL Server 2014 takes off leading zeroes when making Excel file. . . but.

This sp_send_dbmail script works in one of our processes. It attaches an Excel file filled with whatever the query is. It knows to do this because of the extension on the file's name (.xls).
However, it changes a varchar(50) field into a number field, and removes the leading zeroes. This is a known annoyance dealt with in a million ways that won't work for my process.
EXEC msdb.dbo.sp_send_dbmail
#profile_name = #profileName
,#recipients = #emailRecipientList
,#subject = #subject
,#importance = #importance
,#body = #emailMsg
,#body_format = 'html'
,#query = #QuerySQL
,#execute_query_database = #QueryDB
,#attach_query_result_as_file = 1
,#query_attachment_filename = #QueryExcelFileName
,#query_result_header = 1
,#query_result_width = #QueryWidth
,#query_result_separator = #QuerySep
,#query_result_no_padding = 1
Examples of problem below: this simple query changes the StringNumber column from varchar to number in Excel, and removes the zeroes.
SELECT [RowID],[Verbage], StringNumber FROM [dbo].[tblTestStringNumber]
In SQL Server (desired format):
After in Excel (leading zeroes missing):
Now, there might be a way. I only say this because in SQL Server 2016 results pane, if you right click in upper left hand corner, it gives the option of "Open in Excel"
And. . . . drum roll . . . the dataset opens in Excel and the leading zeroes are still there!
If you start a number with a single quote (') in Excel, it will interpret it as a string, so a common solution is to change the query to add one in:
SELECT [RowID]
,[Verbage]
, StringNumber = '''' + [StringNumber]
FROM [dbo].[tblTestStringNumber]
And Excel will usually not display the single quote because it knows that it's a way to cast to type string.
#JustJohn I think it will work fine:
SELECT [RowID]
,[Verbage]
, '="' + [StringNumber]+ '"' StringNumber
FROM [dbo].[tblTestStringNumber]

Violation of PRIMARY KEY constraint. Cannot insert duplicate key in object

I inherited a project and I'm running into a SQL error that I'm not sure how to fix.
On an eCommerce site, the code is inserting order shipping info into another database table.
Here's the code that is inserting the info into the table:
string sql = "INSERT INTO AC_Shipping_Addresses
(pk_OrderID, FullName, Company, Address1, Address2, City, Province, PostalCode, CountryCode, Phone, Email, ShipMethod, Charge_Freight, Charge_Subtotal)
VALUES (" + _Order.OrderNumber;
sql += ", '" + _Order.Shipments[0].ShipToFullName.Replace("'", "''") + "'";
if (_Order.Shipments[0].ShipToCompany == "")
{
sql += ", '" + _Order.Shipments[0].ShipToFullName.Replace("'", "''") + "'";
}
else
{
sql += ", '" + _Order.Shipments[0].ShipToCompany.Replace("'", "''") + "'";
}
sql += ", '" + _Order.Shipments[0].Address.Address1.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.Address2.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.City.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.Province.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.PostalCode.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.Country.Name.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.Phone.Replace("'", "''") + "'";
if (_Order.Shipments[0].ShipToEmail == "")
{
sql += ",'" + _Order.BillToEmail.Replace("'", "''") + "'";
}
else
{
sql += ",'" + _Order.Shipments[0].ShipToEmail.Replace("'", "''") + "'";
}
sql += ", '" + _Order.Shipments[0].ShipMethod.Name.Replace("'", "''") + "'";
sql += ", " + shippingAmount;
sql += ", " + _Order.ProductSubtotal.ToString() + ")";
bll.dbUpdate(sql);
It is working correctly, but it is also outputting the following SQL error:
Violation of PRIMARY KEY constraint 'PK_AC_Shipping_Addresses'. Cannot insert
duplicate key in object 'dbo.AC_Shipping_Addresses'. The duplicate key value
is (165863).
From reading similar questions, it seems that I should declare the ID in the statement.
Is that correct? How would I adjust the code to fix this issue?
I was getting the same error on a restored database when I tried to insert a new record using the EntityFramework. It turned out that the Indentity/Seed was screwing things up.
Using a reseed command fixed it.
DBCC CHECKIDENT ('[Prices]', RESEED, 4747030);GO
I'm pretty sure pk_OrderID is the PK of AC_Shipping_Addresses
And you are trying to insert a duplicate via the _Order.OrderNumber?
Do a
select * from AC_Shipping_Addresses where pk_OrderID = 165863;
or select count(*) ....
Pretty sure you will get a row returned.
It is telling you that you are already using pk_OrderID = 165863 and cannot have another row with that value.
if you want to not insert if there is a row
insert into table (pk, value)
select 11 as pk, 'val' as value
where not exists (select 1 from table where pk = 11)
What is the value you're passing to the primary key (presumably "pk_OrderID")? You can set it up to auto increment, and then there should never be a problem with duplicating the value - the DB will take care of that. If you need to specify a value yourself, you'll need to write code to determine what the max value for that field is, and then increment that.
If you have a column named "ID" or such that is not shown in the query, that's fine as long as it is set up to autoincrement - but it's probably not, or you shouldn't get that err msg. Also, you would be better off writing an easier-on-the-eye query and using params. As the lad of nine years hence inferred, you're leaving your database open to SQL injection attacks if you simply plop in user-entered values. For example, you could have a method like this:
internal static int GetItemIDForUnitAndItemCode(string qry, string unit, string itemCode)
{
int itemId;
using (SqlConnection sqlConn = new SqlConnection(ReportRunnerConstsAndUtils.CPSConnStr))
{
using (SqlCommand cmd = new SqlCommand(qry, sqlConn))
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("#Unit", SqlDbType.VarChar, 25).Value = unit;
cmd.Parameters.Add("#ItemCode", SqlDbType.VarChar, 25).Value = itemCode;
sqlConn.Open();
itemId = Convert.ToInt32(cmd.ExecuteScalar());
}
}
return itemId;
}
...that is called like so:
int itemId = SQLDBHelper.GetItemIDForUnitAndItemCode(GetItemIDForUnitAndItemCodeQuery, _unit, itemCode);
You don't have to, but I store the query separately:
public static readonly String GetItemIDForUnitAndItemCodeQuery = "SELECT PoisonToe FROM Platypi WHERE Unit = #Unit AND ItemCode = #ItemCode";
You can verify that you're not about to insert an already-existing value by (pseudocode):
bool alreadyExists = IDAlreadyExists(query, value) > 0;
The query is something like "SELECT COUNT FROM TABLE WHERE BLA = #CANDIDATEIDVAL" and the value is the ID you're potentially about to insert:
if (alreadyExists) // keep inc'ing and checking until false, then use that id value
Justin wants to know if this will work:
string exists = "SELECT 1 from AC_Shipping_Addresses where pk_OrderID = " _Order.OrderNumber; if (exists > 0)...
What seems would work to me is:
string existsQuery = string.format("SELECT 1 from AC_Shipping_Addresses where pk_OrderID = {0}", _Order.OrderNumber);
// Or, better yet:
string existsQuery = "SELECT COUNT(*) from AC_Shipping_Addresses where pk_OrderID = #OrderNumber";
// Now run that query after applying a value to the OrderNumber query param (use code similar to that above); then, if the result is > 0, there is such a record.
To prevent inserting a record that exist already. I'd check if the ID value exists in the database. For the example of a Table created with an IDENTITY PRIMARY KEY:
CREATE TABLE [dbo].[Persons] (
ID INT IDENTITY(1,1) PRIMARY KEY,
LastName VARCHAR(40) NOT NULL,
FirstName VARCHAR(40)
);
When JANE DOE and JOE BROWN already exist in the database.
SET IDENTITY_INSERT [dbo].[Persons] OFF;
INSERT INTO [dbo].[Persons] (FirstName,LastName)
VALUES ('JANE','DOE');
INSERT INTO Persons (FirstName,LastName)
VALUES ('JOE','BROWN');
DATABASE OUTPUT of TABLE [dbo].[Persons] will be:
ID LastName FirstName
1 DOE Jane
2 BROWN JOE
I'd check if i should update an existing record or insert a new one. As the following JAVA example:
int NewID = 1;
boolean IdAlreadyExist = false;
// Using SQL database connection
// STEP 1: Set property
System.setProperty("java.net.preferIPv4Stack", "true");
// STEP 2: Register JDBC driver
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
// STEP 3: Open a connection
try (Connection conn1 = DriverManager.getConnection(DB_URL, USER,pwd) {
conn1.setAutoCommit(true);
String Select = "select * from Persons where ID = " + ID;
Statement st1 = conn1.createStatement();
ResultSet rs1 = st1.executeQuery(Select);
// iterate through the java resultset
while (rs1.next()) {
int ID = rs1.getInt("ID");
if (NewID==ID) {
IdAlreadyExist = true;
}
}
conn1.close();
} catch (SQLException e1) {
System.out.println(e1);
}
if (IdAlreadyExist==false) {
//Insert new record code here
} else {
//Update existing record code here
}
Not OP's answer but as this was the first question that popped up for me in google, Id also like to add that users searching for this might need to reseed their table, which was the case for me
DBCC CHECKIDENT(tablename)
There could be several things causing this and it somewhat depends on what you have set up in your database.
First, you could be using a PK in the table that is also an FK to another table making the relationship 1-1. IN this case you may need to do an update rather than an insert. If you really can have only one address record for an order this may be what is happening.
Next you could be using some sort of manual process to determine the id ahead of time. The trouble with those manual processes is that they can create race conditions where two records gab the same last id and increment it by one and then the second one can;t insert.
Third, you query as it is sent to the database may be creating two records. To determine if this is the case, Run Profiler to see exactly what SQL code you are sending and if ti is a select instead of a values clause, then run the select and see if you have due to the joins gotten some records to be duplicated. IN any even when you are creating code on the fly like this the first troubleshooting step is ALWAYS to run Profiler and see if what got sent was what you expected to be sent.
Make sure if your table doesn't already have rows whose Primary Key values are same as the the Primary Key Id in your Query.

Resources