Force LINQ to SQL to use RowNumber() instead of Top n When Using .Skip(0) - sql

Is there a way to force LINQ to SQL to avoid using TOP X when using Skip(0)? I have a query that runs just fine for every paged result...except for page 1. I've profiled the query and the introduction of a TOP clause just kills it. I'm perplexed on why that is, but it just does. However, using RowNumber Between 1 AND 10 works just fine.
Is there a way to force LINQ to SQL to avoid using TOP X when using Skip(0)? I have a query that runs just fine for every paged result...except for page 1. I've profiled the query and the introduction of a TOP clause just kills it. I'm perplexed on why that is, but it just does. However, using RowNumber Between 1 AND 10 works just fine.
The culprit seems to be an EXISTS condition in my WHERE clause. The produced SQL is below. In SQL Manager, this query runs fine and returns 14 results...however it times out once I add a TOP 10 (as LINQ would do). However, if I comment the EXISTS in my where clause, then the problem goes away.
SELECT
t0.ProtectiveOrderID,
t3.DocketID,
t3.DocketNumber AS CaseNumber,
t3.PartySuffix AS CaseNumberSuffix,
t5.FirstName AS RespondentNameFirst,
t5.MiddleName AS RespondentNameMiddle,
t5.LastName AS RespondentNameLast,
t5.NameSuffix AS RespondentNameSuffix,
t4.FirstName AS ProtectedNameFirst,
t4.MiddleName AS ProtectedNameMiddle,
t4.LastName AS ProtectedNameLast,
t4.NameSuffix AS ProtectedNameSuffix,
t3.ChildNextFriendFirstName AS ChildNextFriendNameFirst,
t3.ChildNextFriendMiddleName AS ChildNextFriendNameMiddle,
t3.ChildNextFriendLastName AS ChildNextFriendNameLast,
t3.ChildNextFriendNameSuffix
FROM dbo.ProtectiveOrder AS t0
INNER JOIN (
SELECT MAX(t1.ProtectiveOrderID) AS value
FROM dbo.ProtectiveOrder AS t1
GROUP BY t1.DocketID
) AS t2 ON t0.ProtectiveOrderID = t2.value
LEFT OUTER JOIN dbo.Docket AS t3 ON t3.DocketID = t0.DocketID
LEFT OUTER JOIN dbo.Directory AS t4 ON t4.DirectoryID = t3.ProtectedPartyID
LEFT OUTER JOIN dbo.Directory AS t5 ON t5.DirectoryID = t3.SubjectID
WHERE
(
((t4.LastName LIKE 'smith%') AND (t4.FirstName LIKE 'jane%'))
OR ((t5.LastName LIKE 'smith%') AND (t5.FirstName LIKE 'jane%'))
OR ((t3.ChildNextFriendLastName LIKE 'smith%') AND (t3.ChildNextFriendFirstName LIKE 'jane%'))
OR (
-- ***************
-- THIS GUY KILLS THE QUERY WHEN A TOP IS INTRODUCED IN THE TOP-LEVEL SELECT
-- ***************
EXISTS(
SELECT NULL AS EMPTY
FROM dbo.Child AS t6
WHERE (t6.LastName LIKE 'smith%') AND (t6.FirstName LIKE 'jane%') AND (t6.DocketID = t3.DocketID)
)
)
)
ORDER BY t3.DocketNumber

Override the Skip method and just check the input for zero. For any value but zero call the original skip method. For zero don't.
so if you modify the Skip provided in dynamic.cs you could do:
public static IQueryable Skip(this IQueryable source, int count)
{
if (count == 0)
{
return source;
}
if (source == null) throw new ArgumentNullException("source");
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Skip",
new Type[] { source.ElementType },
source.Expression, Expression.Constant(count)));
}

Related

How to cast only the part of a table using a single SQL command in PostgreSQL

In a PostgreSQL table I have several information stored as text. It depends on the context described by a type column what type of information is stored. The application is prepared to get by only one command the Id's of the row.
I got into trouble when i tried to compare the information (bigint stored as a string) with an external value (e.g. '9' > '11'). When I tried to cast the column, the datatbase return an error (not all values in the column are castable, e.g. datetime or normal text). Also when I try to cast only the result of a query command, I get a cast error.
I get the table with the castable rows by this command:
SELECT information.id as id, item.information::bigint as item
FROM information
INNER JOIN item
ON information.id = item.informationid
WHERE information.type = 'task'
The resulting rows are showing up only text that is castable. When I throw it into another command it results in an error.
SELECT x.id FROM (
SELECT information.id as id, item.information::bigint as item
FROM information
INNER JOIN item
ON information.id = item.informationid
WHERE information.type = 'task'
) AS x
WHERE x.item > '0'::bigint
Accroding to the error, the database tried to cast all rows in the table.
Technically, this happens because the optimizer thinks WHERE x.item > '0'::bigint is a much more efficient filter than information.type = 'task'. So in the table scan, the WHERE x.item > '0'::bigint condition is chosen to be the predicate. This thinking is not wrong but will make you fall into this seemingly illogical trouble.
The suggestion by Gordon to use CASE WHEN inf.type = 'task' THEN i.information::bigint END can avoid this, but however it may sometimes ruin your idea to put that as a sub-query and require the same condition to be written twice.
A funny trick I tried is to use OUTER APPLY:
SELECT x.* FROM (SELECT 1 AS dummy) dummy
OUTER APPLY (
SELECT information.id as id, item.information::bigint AS item
FROM information
INNER JOIN item
ON information.id = item.informationid
WHERE information.type = 'task'
) x
WHERE x.item > '0'::bigint
Sorry that I only verified the SQL Server version of this. I understand PostgreSQL has no OUTER APPLY, but the equivalent should be:
SELECT x.* FROM (SELECT 1 AS dummy) dummy
LEFT JOIN LATERAL (
SELECT information.id as id, item.information::bigint AS item
FROM information
INNER JOIN item
ON information.id = item.informationid
WHERE information.type = 'task'
) x ON true
WHERE x.item > '0'::bigint
(reference is this question)
Finally, a more tidy but less flexible method is add the optimizer hint to turn off it to force the optimizer to run the query as how it is written.
This is unfortunate. Try using a case expression:
SELECT inf.id as id,
(CASE WHEN inf.type = 'task' THEN i.information::bigint END) as item
FROM information inf JOIN
item i
ON inf.id = i.informationid
WHERE inf.type = 'task';
There is no guarantee that the WHERE filter is applied before the SELECT. However, CASE does guarantee the order of evaluation, so it is safe.

Select inside select not working properly and shows error

I have 2 tables:
t_orderPayment with n_id,n_order,n_paytype AND
t_paytype with n_ID , str_desc
My Query is:
SELECT t_OrderPayment.n_order ,
t_OrderPayment.n_paytype = (select t_paytype.str_desc as n_paytype from t_PayType, t_orderpayment where t_OrderPayment.n_PayType = t_PayType.n_ID)
FROM t_OrderPayment
WHERE (((t_OrderPayment.n_PaymentStatus)<>-7)) ;
It returns error
"At most one record can be returned by this subquery"
I tried to set it with MAX and TOP 1 like n_paytype = (select max(t_paytype.str_desc) but shows me error Expr1001
Str.desc have only text values and this could be the reason. May be INNER JOIN can help me somehow?
It looks like you've accidentally made a cross join. You don't need a second reference to the t_OrderPayment table inside the subquery - try removing that (but leaving the where clause as that's what will link it to the outer query). It looks like the whole subquery could be removed and turned into a join though...
SELECT t_OrderPayment.n_order,
t_paytype.str_desc AS n_paytype
FROM t_OrderPayment
INNER JOIN t_PayType
ON t_OrderPayment.n_PayType = t_PayType.n_ID
WHERE t_OrderPayment.n_PaymentStatus <> -7;
SELECT t_OrderPayment.n_order,
t_paytype.str_desc AS n_paytype
FROM t_OrderPayment, t_PayType
WHERE t_OrderPayment.n_PayType = t_PayType.n_ID AND
t_OrderPayment.n_PaymentStatus <> -7;

MS Access - SQL LEFT JOIN multiple conditions

I have this code which is working fine except that I need to add one more condition:
SELECT record1.*,
tbl_mpsregion.maintenanceteam,
tbl_mpsregion.regionmps
INTO tbl_sapforecast
FROM tbl_mpsregion
RIGHT JOIN
(
SELECT sap_ip19.*,
dateserial(RIGHT(trim([SAP_IP19].[PlanDate]),4),mid(trim([SAP_IP19].[PlanDate]),4,2),LEFT(trim([SAP_IP19].[PlanDate]),2)) AS [DATE/FORECAST],
tbl_labourstandard.re,
tbl_labourstandard.manning,
tbl_labourstandard.skillset AS skillset,
tbl_regionmapping.maintenanceplant,
tbl_regionmapping.area,
tbl_regionmapping.region AS region,
tbl_regionmapping.onresponse,
[RE]*[Manning]/60 AS hours
FROM (sap_ip19
LEFT JOIN tbl_labourstandard
ON (
LEFT(sap_ip19.[Task list description],3) = tbl_labourstandard.jemenawc)
AND (
sap_ip19.[MntPlan] = cdbl(tbl_labourstandard.supplypoint )))
LEFT JOIN tbl_regionmapping
ON sap_ip19.location = cdbl([Tbl_RegionMapping].[FittersDistricts])) AS record1
ON (
record1.region = [Tbl_MPSRegion].[Region])
AND (
record1.skillset = [Tbl_MPSRegion].[Skillset]) ;
Criteria to add is: If SAP_IP19.MntPlan does not match Tbl_LabourStandard.SupplyPoint then use 0 for Tbl_LabourStandard.SupplyPoint. I am not using Server 2000 so using CASE is not a solution. Have tried IIF and SWITCH but they are not taking query to sleep mode (not evaluating). I read that JOINS with IIF or SWITCH cannot be used. Please help!
You should be able to add if's or switches but you could always handle this with an OR - it's not the most performance friendly but it should get the job done, example below:
LEFT JOIN tbl_labourstandard
ON
(LEFT(sap_ip19.[Task list description],3) = tbl_labourstandard.jemenawc)
AND
((Tbl_LabourStandard.SupplyPoint = SAP_IP19.MntPlan AND
sap_ip19.[MntPlan] = cdbl(tbl_labourstandard.supplypoint))
OR (sap_ip19.[MntPlan] = 0))

single-row subquery returns more than one row. Query not working with main query

I hve to display several cell values into one cell. So I am using this query:
select LISTAGG(fc.DESCRIPTION, ';'||chr(10))WITHIN GROUP (ORDER BY fc.SWITCH_NAME) AS DESCRIP from "ORS".SWITCH_OPERATIONS fc
group by fc.SWITCH_NAME
It is working fine. But when I am merging this with my main(complete) query then I am getting the error as: Error code 1427, SQL state 21000: ORA-01427: single-row subquery returns more than one row
Here is my complete query:
SELECT
TRACK_EVENT.LOCATION,
TRACK_EVENT.ELEMENT_NAME,
(select COUNT(*) from ORS.TRACK_EVENT b where (b.ELEMENT_NAME = sw.SWITCH_NAME)AND (b.ELEMENT_TYPE = 'SWITCH')AND (b.EVENT_TYPE = 'I')AND (b.ELEMENT_STATE = 'NORMAL' OR b.ELEMENT_STATE = 'REVERSE'))as COUNTER,
(select COUNT(*) from ORS.SWITCH_OPERATIONS fc where TRACK_EVENT.ELEMENT_NAME = fc.SWITCH_NAME and fc.NO_CORRESPONDENCE = 1 )as FAIL_COUNT,
(select MAX(cw.COMMAND_TIME) from ORS.SWITCH_OPERATIONS cw where ((TRACK_EVENT.ELEMENT_NAME = cw.SWITCH_NAME) and (cw.NO_CORRESPONDENCE = 1)) group by cw.SWITCH_NAME ) as FAILURE_DATE,
(select LISTAGG(fc.DESCRIPTION, ';'||chr(10))WITHIN GROUP (ORDER BY fc.SWITCH_NAME) AS DESCRIP from "ORS".SWITCH_OPERATIONS fc
group by fc.SWITCH_NAME)
FROM
ORS.SWITCH_OPERATIONS sw,
ORS.TRACK_EVENT TRACK_EVENT
WHERE
sw.SEQUENCE_ID = TRACK_EVENT.SEQUENCE_ID
Not only are subqueries in the SELECT list required to return exactly one row (or any time they're used for a singular comparison, like <, =, etc), but their use in that context tends to make the database execute them RBAR - Row-by-agonizing-row. That is, they're slower and consume more resources than they should.
Generally, unless the result set outside the subquery contains only a few rows, you want to construct subqueries as part of a table-reference. Ie, something like:
SELECT m.n, m.z, aliasForSomeTable.a, aliasForSomeTabe.bSum
FROM mainTable m
JOIN (SELECT a, SUM(b) AS bSum
FROM someTable
GROUP BY a) aliasForSomeTable
ON aliasForSomeTable.a = m.a
This benefits you in other ways to - it's easier to get multiple columns out of the same table-reference, for example.
Assuming that LISTAGG(...) can be included with other aggregate functions, you can change your query to look like this:
SELECT Track_Event.location, Track_Event.element_name,
Counted_Events.counter,
Failure.fail_count, Failure.failure_date, Failure.descrip
FROM ORS.Track_Event
JOIN ORS.Switch_Operations
ON Switch_Operations.sequence_id = Track_Event.sequence_id
LEFT JOIN (SELECT element_name, COUNT(*) AS counter
FROM ORS.Track_Event
WHERE element_type = 'SWITCH'
AND event_type = 'I'
AND element_state IN ('NORMAL', 'REVERSE')
GROUP BY element_name) Counted_Events
ON Counted_Events.element_name = Switch_Operations.swicth_name
LEFT JOIN (SELECT switch_name,
COUNT(CASE WHEN no_correspondence = 1 THEN '1' END) AS fail_count,
MAX(CASE WHEN no_correspondence = 1 THEN command_time END) AS failure_date,
LISTAGG(description, ';' || CHAR(10)) WITHIN GROUP (ORDER BY command_time) AS descrip
FROM ORS.Switch_Operations
GROUP BY switch_name) Failure
ON Failure.switch_name = Track_Event.element_name
This query was written to (attempt to) preserve the semantics of your original query. I'm not completely sure that's what you actually need but without sample starting data and desired results, I have no way to tell how else to improve this. For instance, I'm a little suspicious of the need of Switch_Operations in the outer query, and the fact that LISTAGG(...) is run over row where no_correspondence <> 1. I did change the ordering of LISTAGG(...), because the original column would not have done anything (because the order way the same as the grouping), so would not have been a stable sort.
Single-row subquery returns more than one row.
This error message is self descriptive.
Returned field can't have multiple values and your subquery returns more than one row.
In your complete query you specify fields to be returned. The last field expects single value from the subquery but gets multiple rows instead.
I have no clue about the data you're working with but either you have to ensure that subquery returns only one row or you have to redesign the wrapping query (possibly using joins when appropriate).

Whats wrong with this nested query?

I am trying to write a query to return the id of the latest version of a market index stored in a database.
SELECT miv.market_index_id market_index_id from ref_market_index_version miv
INNER JOIN ref_market_index mi ON miv.market_index_id = mi.id
WHERE mi.short_name='dow30'
AND miv.version_num = (SELECT MAX(m1.version_num) FROM ref_market_index_version m1 INNER JOIN ref_market_index m2 ON m1.market_index_id = m2.id )
The above SQL statement can be (roughly) translated into the form:
SELECT some columns FROM SOME CRITERIA MATCHED TABLES
WHERE mi.short_name='some name'
AND miv.version_num = SOME NUMBER
What I don't understand is that when I supply an actual number (instead of a sub query), the SQL statement works - also, when I test the SUB query used to determine the latest version number, that also works - however, when I attempt to use the result returned by sub query in the outer (parent?) query, it returns 0 rows - what am I doing wrong here?
Incidentally, I also tried an IN CLAUSE instead of the strict equality match i.e.
... AND miv.version_num IN (SUB QUERY)
That also resulted in 0 rows, although as before, when running the parent query with a hard coded version number, I get 1 row returned (as expected).
BTW I am using postgeresql, but I prefer the solution to be db agnostic.
The problem is probably that the max(version_num) doesn't exist for 'dow30'.
Try the following correlated subquery:
SELECT miv.market_index_id market_index_id
from ref_market_index_version miv INNER JOIN
ref_market_index mi
ON miv.market_index_id = mi.id
WHERE mi.short_name='dow30' AND
miv.version_num = (SELECT MAX(m1.version_num)
FROM ref_market_index_version m1 INNER JOIN
ref_market_index m2
ON m1.market_index_id = m2.id
where m1.short_name = 'dow30'
)
I added the where clause in the subquery.