I am trying to improve the performance of a very large and complex query. Below is the relevant portions. I pass the id to the where clause and get back an orderid. I need to get the order comments from another database on a linked server. I understand that I have to pass the query string to OpenQuery, it cannot have dynamic values. In my example I've hard coded it.
How do I get the S.OrderId value and pass it to the OpenQuery? I've tried some of the example but none do this with a subquery. Declare and Set throw errors inside of my main SELECT.
SELECT S.ID AS Id
, S.OrderID
, (SELECT * FROM OPENQUERY(SQL2014A, 'SELECT TOP 1 SECCOMMENT FROM TMWAMS.dbo.ORDERSEC WHERE ORDERID = 1515552')) AS COMMENTS
FROM ShopPO S WHERE ID = 230
Related
I need to do a sum on the columns MTH1 MTH2 etc but for the parameters I pass it will produce six rows of information for the same account code which is correct and what it should produce my question is how do I only return one of data but with all the sum of the six rows this is what I have so far for my stored procedure.
ALTER PROCEDURE [dbo].[sumbalances]
#AccountRef VARCHAR(500),
#SortOrder INT,
#CATEGORY INT
AS
BEGIN
SET NOCOUNT ON;
SELECT
NOMINAL_LEDGER.ACCOUNT_REF, NOMINAL_LEDGER.NAME,
NOMINAL_LEDGER.ACCOUNT_TYPE, NOMINAL_LEDGER.BALANCE,
NOMINAL_LEDGER.QUICK_RATIO, NOMINAL_LEDGER.SOFA_ID,
NOMINAL_LEDGER.PRIOR_YR_MTH1, NOMINAL_LEDGER.PRIOR_YR_MTH2,
NOMINAL_LEDGER.PRIOR_YR_MTH3, NOMINAL_LEDGER.PRIOR_YR_MTH4,
NOMINAL_LEDGER.PRIOR_YR_MTH5, NOMINAL_LEDGER.PRIOR_YR_MTH6,
NOMINAL_LEDGER.PRIOR_YR_MTH7, NOMINAL_LEDGER.PRIOR_YR_MTH8,
NOMINAL_LEDGER.PRIOR_YR_MTH9, NOMINAL_LEDGER.PRIOR_YR_MTH10,
NOMINAL_LEDGER.PRIOR_YR_MTH11, NOMINAL_LEDGER.PRIOR_YR_MTH12,
NOMINAL_LEDGER.PRIOR_YR2_MTH1, NOMINAL_LEDGER.PRIOR_YR2_MTH2,
NOMINAL_LEDGER.PRIOR_YR2_MTH3,
FROM
CATEGORY
LEFT JOIN
NOMINAL_LEDGER ON CATEGORY.CompanyID = NOMINAL_LEDGER.CompanyID
WHERE
ACCOUNT_REF = #AccountRef
AND SORT_ORDER = #SortOrder
AND CATEGORY = #CATEGORY
END
I presume that I would need some kind of loop to save the sum off all the MTH1's or would it not need to be as complicated as that.
Below is example of the data.
So for example if it was the Mth 1 it would add
40000.00
44000.00
And returns that total in the MTH 1 column but only one row for all of them being summed up if that make since.
Edit 2
Please see my sql fiddle here I am having some trouble linking the category table if someone could help be great. that is not the main issue of the above though so you guys can see the data
http://sqlfiddle.com/#!18/6c902/1
You seem to simply want a GROUP BY. But you can also simplify the query in other ways:
select nl.ACCOUNT_REF,
sum(nl.PRIOR_YR_MTH1) as PRIOR_YR_MTH1,
sum(nl.PRIOR_YR_MTH2) as PRIOR_YR_MTH2,
. . . - fill in the rest of the months
from NOMINAL_LEDGER nl join
CATEGORY c
on c.CompanyID = nl.CompanyID
where nl.ACCOUNT_REF = #AccountRef and
nl.SORT_ORDER = #SortOrder and
c.CATEGORY = #CATEGORY
group by nl.ACCOUNT_REF;
Notes:
The WHERE clause (presumably) undoes the LEFT JOIN, turning it into an INNER JOIN. So use the proper JOIN.
Table aliases make the query easer to write and to read.
You seem to want one row per account, so the query aggregates by account and leaves out the other non-aggregated columns.
Salesforce Marketing Cloud queries do not allow variables or temporary tables according to the "SQL Support" section of this official documentation (http://help.marketingcloud.com/en/documentation/exacttarget/interactions/activities/query_activity/)
I have a data extension called Parameters_DE with fields Name and Value that stores constant values. I need to refer to this DE in queries.
Using transact-SQL, an example is:
Declare #number INT
SET #number = (SELECT Value FROM Parameters_DE WHERE Name='LIMIT')
SELECT * FROM Items_DE
WHERE Price < #number
How can the above be done without variables or temporary tables so that I can refer to the value of the 'LIMIT' variable that is stored in Parameters_DE and so that the query will work in Marketing Cloud?
This is what I would have done anyway, even if variables are allowed:
SELECT i.*
FROM Items_DE i
INNER JOIN Parameters_DE p ON p.Name = 'LIMIT'
WHERE i.Price < p.Value
Wanting to a use a variable is indicative of still thinking procedural, instead of set-based. Note that, if you need to, you can join to the Parameters_DE table more than once (give a difference alias each time) to use the values of different parameters at different parts in a query.
You can also make things more efficient for this type of query by having a parameters table with one row, and a column for each value you need. Then you can JOIN to the table one time with a 1=1 condition and look at just the columns you need. Of course, this idea has limitations, too.
You could just use the SELECT which retrieves the number in your WHERE clause:
SELECT * FROM Items_DE
WHERE Price < (SELECT Value FROM Parameters_DE WHERE Name='LIMIT')
This can be done with a join
SELECT i.*
FROM Items_DE i
INNER JOIN Parameters_DE p
ON p.Name = 'LIMIT'
AND p.Price > i.Value
I have an SQL statement for a PICK sheet that returns the header/detail records for an order.
One of the fields in the SQL is basically a field to say if there are dangerous goods. If a single product on the order has a code against it, then the report should display that its hazardous.
The problem I am having is that in the SQL results, because I am putting the code on the report in the header section (and not the detail section), it is looking for the code only on the first row.
Is there a way through SQL to basically say "if one of these rows has this code, make all of these rows have this code"? I'm guessing a subselect would work here... the problem is, is that I am using a legacy system built on FoxPro and FoxPro SQL is terrible!
EDIT: just checked and I am running VFP8, subqueries in the SELECT statement were added in FVP9 :(
SELECT Header.HeaderId, Header.HeaderDescription,
Detail.DetailId, Detail.DetailDescription, Detail.Dangerous,
Danger.DangerousItems
FROM Header
INNER JOIN Detail ON Header.HeaderId = Detail.HeaderId
LEFT OUTER JOIN
(SELECT HeaderId, COUNT(*) AS DangerousItems FROM Detail WHERE Dangerous = 1 GROUP BY HeaderId) Danger ON Header.HeaderId = Danger.HeaderId
If Danger.DangerousItems > 0 then something is dangerous. If it is Null then nothing is dangerous.
If you can't do nested queries, then you should be able to create a view-like object (called a query in VFP8) for the nested select:
SELECT HeaderId, COUNT(*) AS DangerousItems FROM Detail WHERE Dangerous = 1 GROUP BY HeaderId
and then can you left join on that?
In VFP 8 and earlier, your best bet is to use three queries in a row:
SELECT Header.HeaderId, Header.HeaderDescription,
Detail.DetailId, Detail.DetailDescription, Detail.Dangerous,
Danger.DangerousItems
FROM Header
INNER JOIN Detail ON Header.HeaderId = Detail.HeaderId
INTO CURSOR csrDetail
SELECT HeaderId, COUNT(*) AS DangerousItems
FROM Detail
WHERE Dangerous
GROUP BY HeaderId
INTO CURSOR csrDanger
SELECT csrDetail.*, csrDanger.DangerousItems
FROM csrDetail.HeaderID = csrDanger.HeaderID
INTO CURSOR csrResult
I have a view name "vw_AllJobsWithRecruiter".
ALTER VIEW dbo.vw_AllJobsWithRecruiter
AS
SELECT TOP(SELECT COUNT(iJobID_PK) FROM dbo.tbUS_Jobs)
iJobId_PK AS JobId,
dbo.ufn_JobStatus(iJobId_PK) AS JobStatus,
dbo.ufn_RecruiterCompanyName(iJobId_PK) AS CompanyName,
sOther AS OtherCompanyName
FROM dbo.tbUS_Jobs
WHERE bDraft = 0
ORDER BY dtPostedDate DESC
This view contains only 3278 number of rows.
If I execute the below query :
SELECT * FROM vw_AllJobsWithRecruiter
WHERE OtherCompanyName LIKE '%Microsoft INC%'
It is taking less than a second to execute.
Now my problem is:
If I use the query below query:
SELECT * FROM vw_AllJobsWithRecruiter
WHERE CompanyName LIKE '%Microsoft INC%'
OR OtherCompanyName LIKE '%Microsoft INC%'
It is taking 30 seconds to execute and from the front end it is throwing timeout error.
The function is here:
CREATE Function [dbo].[ufn_RecruiterCompanyName] (#JobId bigint)
RETURNS nvarchar(200)
AS
BEGIN
DECLARE #ResultVar nvarchar(200)
DECLARE #RecruiterId bigint
select #RecruiterId = iRecruiterId_FK from dbo.tbUS_Jobs with (Nolock)
where iJobId_PK = #JobId;
Select #ResultVar = sCompanyName from dbo.tbUS_RecruiterCompanyInfo with (Nolock)
where iRecruiterId_FK = dbo.ufn_GetParentRecruiterID(#RecruiterId)
return isnull(#ResultVar,'')
END
The other function
CREATE Function [dbo].[ufn_GetParentRecruiterID](#RecruiterId bigint)
returns bigint
as
begin
declare #ParentRecruiterId bigint
SELECT #ParentRecruiterId = iParentId FROM dbo.tbUS_Recruiter with (Nolock)
WHERE iRecruiterId_PK = #RecruiterId
IF(#ParentRecruiterId = 0)
SET #ParentRecruiterId = #RecruiterId
RETURN #ParentRecruiterId
end
My questions are
Why it is taking so much time to execute?
How can I reduce the execution time?
Thanks a lot for your attention.
The first query only calls dbo.ufn_RecruiterCompanyName() only for the rows returned, it filters on a stored value. For the second Query, SQL Server needs to call the ufn for all of the rows. Depending on the function, this might cause the delay.
Check this in Query Analyzer, and try to avoid the second Query ^^
After taking a look at the custom function I suggest rewriting that View using joined tables. When doing the lookups in such functions, SQL Server calls them for every Row that it touches or delivers. Using a LEFT JOIN allows the Server to use the Indexes and Key much faster and should deliver the Data in less than a second.
Without all of the custom functions and a Definition of all the tables, I cannot give you an Example of that new View, but it should look a bit like this:
SELECT
jobs.Jobid,
jobstatus.Jobstatus,
recruiter.Company
FROM jobs
LEFT JOIN jobstatus ON jobs.Jobid = jobstatus.Jobid
LEFT JOIN recruiter ON jobs.Recruiterid = recruiter.Recruiterid
The problem is your nested function calls.
You are calling ufn_RecruiterCompanyName in your WHERE clause, albeit indirectly.
What this means is, your WHERE clause is non-Sargable, and must run that function for every row.
That function also calls ufn_GetParentRecruiterID. Since that is in your WHERE clause within the first function, and also non-Sargable, you are basically doing two table scans per row in your table.
Replace the function calls with JOINs and you will see a huge boost in performance.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
My job is the maintain one application which heavy use SQL server (MSSQL2005).
Until now middle server stores TSQL codes in XML and send dynamic TSQL queries without using stored procs.
As I am able change those XML queries I want to migrate most of my queries to stored procs.
Question is folowing:
Most of my queries have same Where conditions against one table
Sample:
Select
.....
from ....
where ....
and (a.vrsta_id = #vrsta_id or #vrsta_id = 0)
and (a.podvrsta_id = #podvrsta_id or #podvrsta_id = 0)
and (a.podgrupa_2 = #podgrupa2_id or #podgrupa2_id = 0)
and (
(a.id in (select art_id from osobina_veze where podosobina_id in (select ado from dbo.fn_ado_param_int(#podosobina))
group by art_id
having count(art_id)= #podosobina_count ))
or ('0' = #podosobina)
)
They also have same where conditions on other table.
How I should organize my code ?
What is proper way ?
Should I
make table valued function that I will use in all queries
or use #Temp tables and simple inner join my query to that each time when proc executing?
or use #temp filed by table valued function ?
or leave all queries with this large where clause and hope that index is going to do their jobs.
or use WITH(statement)
i've come to realize that having such complex searches in a single query is not really a good idea.
i prefer to construct the sql depeding on the input condition values.
this makes it easier fo rsql server to construct a better execution plan for each search.
this way i'd bet that you have a sub-optimal execution plan for your queries.
i realize that this would include dynamic sql so the usual warnings for it apply.
You have two different functional concerns here: the selection of values from your one table and the choice of columns to be returned or other tables to be joined to that data. If the number of items from filtering on your one table could be large, I would inclined to store the PK of selected values into a middle or work table. If it is a permanent table, you can separate different searches by something like SessionId or you could just separate each set of search results by a random value which you pass from the filtering routine to the selecting routine.
There is no reason you could not keep the filtering routine in dynamic SQL. However, I would not try to do dynamic SQL in T-SQL. T-SQL is awful for string manipulation. Building the queries dynamically from your middle tier affords you the ability to exclude elements from the Where clause that are effectively not passed. E.g., instead of having and (a.vrsta_id = #vrsta_id or #vrsta_id = 0), you could simply exclude this line altogether when #vrsta_id is in fact zero or have a.vrsta_id = #vrsta_id when #vrsta_id is not zero. Generally, this type of query will perform better than a series of ORs.
Once you have your work table, your selecting queries would look something like:
Select..
From WorkTable As W
Join ...
Where SetId = 12345
And ( OtherTable.Col = ....
In this case, SetId would represent the set of items that were created from the filtering routine.
You can create a table valued function that takes in the parameters and returns a table of matching a.id values. Then you can inner join that function onto the query in each of your stored procedures. For example:
create function dbo.GetMatches
(
#vrsta_id int,
#podvrsta_id int,
#podgrupa2_id int,
#podosobina_count int
)
returns table
as
return
Select
a.id
from a
where
(a.vrsta_id = #vrsta_id or #vrsta_id = 0)
and (a.podvrsta_id = #podvrsta_id or #podvrsta_id = 0)
and (a.podgrupa_2 = #podgrupa2_id or #podgrupa2_id = 0)
and (
(a.id in (select art_id from osobina_veze where podosobina_id in (select ado from dbo.fn_ado_param_int(#podosobina))
group by art_id
having count(art_id)= #podosobina_count ))
or ('0' = #podosobina)
)
Then in this example query...
select
*
from
a
inner join dbo.GetMatches(1,2,3,4) matches
on a.id = matches.id
inner join b on a.bID = b.bID -- example other table
You could also use that function in the where statement like this...
where
a.id in (select id from dbo.GetMatches(1,2,3,4))