SQL - Make this statement faster i.e. less duration, reads - linq-to-sql

Given the following tables:
Orders (OrderID, OrderStatus, OrderNumber)
OrderItems(OrderItemID, OrderID, ItemID, OrderItemStatus)
Orders: 2537 records
Order Items: 1319 records
I have created indexes on
Orders(OrderStatus)
OrderItems(OrderID)
OrderItems(OrderItemStatus)
I have the following SQL statement (generated by LinqToSql) which when executed, has:
- duration = 8789
- reads = 7809.
exec sp_executesql N'SELECT COUNT(*) AS [value]
FROM [dbo].[Orders] AS [t0]
WHERE ([t0].[OrderStatus] = #p0) OR (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[OrderItems] AS [t1]
WHERE ([t1].[OrderID] = [t0].[OrderID]) AND ([t1].[OrderItemStatus] = #p1)
))',N'#p0 nvarchar(2),#p1 nvarchar(2)',#p0=N'KE',#p1=N'KE'
Is there anything else which I can do to make it faster?

make all those nvarchars parameters varchars if the columns in the table are varchars
))',N'#p0 varchar(2),#p1 varchar(2)',#p0=N'KE',#p1=N'KE'
See also here: sp_executesql causing my query to be very slow

Count on a single index rather than *

This might generate some better sql.
IQueryable<int> query1 =
from oi in db.OrderItems
where oi.OrderItemStatus == theItemStatus
select oi.OrderID;
IQueryable<int> query2 =
from o in db.Orders
where o.OrderStatus == theOrderStatus
select o.OrderID;
IQueryable<int> query3 = query1.Concat(query2).Distinct();
int result = query3.Count();

Related

How can you reference a column based on the average on another column in MySQL?

We have a scenario where users answer some questions related to a parent entity that we'll call a widget. Each question has both a numeric and word answer. Multiple users answer each question for a given widget.
We then display a row for each widget with the average numeric answer for each question. We do that using a MySQL pseudo-pivot with dynamic columns as detailed here So we end up with something like:
SELECT widget_id, ...
ROUND(IFNULL(AVG(CASE
WHEN LOWER(REPLACE(RQ.question, ' ', '_')) = 'overall_size' THEN
if(RA.num = '', 0, RA.num) END),0) + .0001, 2) AS `raw_avg_overall_size`,
...
... where overall_size would be one of the question types related to the widget and might have "answers" from 5 users like 1,2,2,3,1 to that question for a given widget_id based on the answer options below:
Answers
answer_id
answer_type
num
word
111
overall_size
1
x-large
112
overall_size
2
large
113
overall_size
3
medium
114
overall_size
4
small
115
overall_size
5
x-small
So we would end up with a row that had something like this:
widget_id
average_overall_size
115
1.80
What we can't figure out is then given if we round 1.80 to zero precision we get 2 in this example which is the word value 'large' from our data above. We like to include that in the query output too so that end up with:
widget_id
raw_average_overall_size
average_overall_size
115
1.80
large
The issue is that we do not know the average for the row until the query runs. So how can we then reference the word value for that average answer in the same row when executing the query?
As mentioned we are pivoting into a variable and then run another query for the full execution. So if we join in the pivot section, that subquery looks something like this:
SET #phase_id = 1;
SET SESSION group_concat_max_len = 100000;
SET #SQL = NULL;
SET #NSQL = NULL;
SELECT GROUP_CONCAT(DISTINCT
CONCAT(
'ROUND(IFNULL(AVG(CASE
WHEN LOWER(REPLACE(RQ.short_question, '' '', ''_'')) = ''',
nsq,
''' THEN
if(RA.answer = '''', 0, RA.answer) END),0) + .0001, 2) AS `',
CONCAT('avg_raw_',nsq), '`,
REF.value, -- <- ******* THIS FAILS **** --
ROUND(IFNULL(STDDEV(CASE
WHEN LOWER(REPLACE(RQ.short_question, '' '', ''_'')) = ''',
nsq,
''' THEN RA.answer END), 0) + .0001, 3) AS `',
CONCAT('std_dev_', nsq), '`
'
)
ORDER BY display_order
) INTO #NSQL
FROM (
SELECT FD.ref_value, FD.element_name, RQ.display_order, LOWER(REPLACE(RQ.short_question, ' ', '_')) as nsq
FROM review_questions RQ
LEFT JOIN form_data FD ON FD.id = RQ.form_data_id
LEFT JOIN ref_values RV on FD.ref_value = RV.type
WHERE RQ.phase_id = #phase_id
AND FD.element_type = 'select'
AND RQ.is_active > 0
GROUP BY FD.element_name
HAVING MAX(RV.key_name) REGEXP '^[0-9]+$'
) nq
/****** suggested in 1st answer ******/
LEFT JOIN ref_values REF ON REF.`type` = nq.ref_value
AND REF.key_name = ROUND(CONCAT('avg_raw_',nsq), 0);
So we need the word answer (from the REF join's REF.value field in the above code) in the pivot output, but it fails with 'Unknown column REF.value. If we put REF.value in it's parent query field list, that also fails with the same error.
You'll need to join the table/view/query again to get the 'large' value.
For example:
select a.*, b.word
from (
-- your query here
) a
join my_table b on b.answer_id = a.answer_id
and b.num = round(a.num);
An index on my_table (answer_id, num) will speed up the extra search.
This fails, leading to the default of "2":
LOWER(REPLACE(RQ.question, ' ', '_')) = 'overall_size'
That is because the question seems to be "average_overall_size", not "overall_size".
String parsing and manipulation is the pits in SQL; suggest using the application to handle such.
Also, be aware that you may need a separate subquery to compute aggregate (eg AVG()), else it might not be computed over the set of values you think.
Query into temp table, then join
First query should produce table as follows:
CREATE temp table, temp_average_size
widget_id
average_overall_size
rounded_average_size
115
1.80
2
LEFT JOIN
select s.*, a.word
from temp_average_size s LEFT JOIN answers a
ON (s.rounded_average_size = a.num AND a.answer_type = 'overall_size)

MySQL best select multi condition query

I have a MySQL query:
SELECT * FROM product_variants WHERE (bottom_type = '$bottom_type1' OR bottom_type = '$bottom_type2' ) AND (bottom_size = '$bottom_size ' ) AND (product_id != '$product_id1 ' OR product_id != '$product_id2' OR product_id != '$product_id3')
It is not working as it should, its not giving me the results needed.
What is the best way to build it?
Thanks
This is just my guess at what you're after. Example table structure, with data and expected results would help answer your question.
SELECT *
FROM product_variants
WHERE bottom_type IN ('$bottom_type1', '$bottom_type2')
AND bottom_size = '$bottom_size'
AND product_id NOT IN ('$product_id1 ', '$product_id2', '$product_id3')

Zend Framework 2 - sql subquery

I'm trying to run a special SQL query in ZF 2.
SELECT listingId, COUNT(*) as num
FROM
(SELECT DISTINCT listingId, locationId
FROM l_f_locations
WHERE locationId IN ( 7, 9, 10)) AS foo
GROUP by listingId, HAVING num = 3
I tried creating the subquery first as it's a complete MySQL query but then fail to integrate it into the main query at all. I can't alias the subquery e.g. "AS foo" as this is a requirement for the complete SQL squery to work.
Any ideas?
First of all, you can do this without a sub-query:
SELECT listingId, COUNT(DISTINCT locationId) AS num
FROM l_f_locations
WHERE listingId IN(7,9,10)
GROUP BY listingId
HAVING num = 3;
For future reference, however, you could do the query you mention using a pair of Zend_Db_Select objects, one for the sub-query and another for the main:
$subQuery = $dbAdapter->select()
->from('l_f_locations', array('listingId', 'locationId'))
->where('locationId IN(7,9,10)')
->group('listingId')
->group('locationId');
$select = $dbAdapter->select()
->from($subQuery, array('*', 'num' => 'COUNT(*)'))
->group('listingId')
->having('num = 3');
$result = $select->query()->fetchAll();

Equivalent query in sql server for INFORMATION_SCHEMA

I am working on a page where we need to visually compare the schema of same table across the two database-one in sql server and other in mysql.I have to include indexes as well.
now mysql query shows info along with indexes -
select column_name,column_type,table_name,column_key
from INFORMATION_SCHEMA.COLUMNS where table_name = 'tbl_ClientDN'
But for sql server the same query does not return indexes-
select * from INFORMATION_SCHEMA.COLUMNS where table_name = 'tbl_ClientDN'
so i need query to compbine the result of -
sp_helpindex 'tbl_ClientDN'
how to get column_key showing indexes in mssql query.
any suggestion ?
Stay away from INFORMATION_SCHEMA.COLUMNS, especially for indexes, since things like filtered indexes and included columns are not part of the definition. I talk about this in more detail here:
The case against INFORMATION_SCHEMA views
You want to use sys.indexes and sys.index_columns for this. For example:
DECLARE #tablename NVARCHAR(512) = 'dbo.tbl_ClientDN';
SELECT
[Index] = i.name,
[Column] = c.Name,
[Type] = i.type_desc,
PK = i.is_primary_key,
[Unique] = i.is_unique,
[Unique Constraint] = i.is_unique_constraint,
[DESC] = ic.is_descending_key,
[INCLUDE] = ic.is_included_column,
[Filtered] = i.filter_definition -- only for SQL Server 2008+
FROM
sys.indexes AS i
INNER JOIN
sys.index_columns AS ic
ON i.[object_id] = ic.[object_id]
AND i.index_id = ic.index_id
INNER JOIN
sys.columns c
ON ic.column_id = c.column_id
AND ic.[object_id] = c.[object_id]
WHERE
i.[object_id] = OBJECT_ID(#tablename)
ORDER BY [Index], ic.index_column_id;
If you want to do this for all tables at once, then simple changes:
SELECT
[Table] = QUOTENAME(OBJECT_SCHEMA_NAME(i.[object_id]))
+ '.' + QUOTENAME(OBJECT_NAME(i.[object_id])),
[Index] = i.name,
[Column] = c.Name,
[Type] = i.type_desc,
PK = i.is_primary_key,
[Unique] = i.is_unique,
[Unique Constraint] = i.is_unique_constraint,
[DESC] = ic.is_descending_key,
[INCLUDE] = ic.is_included_column,
[Filtered] = i.filter_definition -- only for SQL Server 2008+
FROM
sys.indexes AS i
INNER JOIN
sys.index_columns AS ic
ON i.[object_id] = ic.[object_id]
AND i.index_id = ic.index_id
INNER JOIN
sys.columns c
ON ic.column_id = c.column_id
AND ic.[object_id] = c.[object_id]
ORDER BY [Table], [Index], ic.index_column_id;
More information available in the topics sys.indexes and sys.index_columns.
You also might want to take a look at Kimberley L. Tripp's sp_helpindex2.
EDIT
In general I agree with #BrianWhite's comment. If you are spending any effort on this at all, you should be using a tool for this instead of re-inventing the wheel and trying to write it yourself. Troubleshooting this one query you've probably already spent, in terms of time, the cost of a good tool. Please read this post:
The cost of reinventing the wheel
Perhaps you are after sp_help
http://msdn.microsoft.com/en-us/library/ms187335.aspx
Simply entering
sp_help myTableName
should give you all the data you could want.

Error creating a SP with temporal table inside and subquery

I've created this temporal table in my store procedure, as you can see I have more than 1 records for the same ID:
#tmpTableResults
TmpInstallerID TmpConfirmDate TmpConfirmLocalTime
============== ============== ===================
173 2011-11-08 11:45:50
278 2011-11-04 09:06:26
321 2011-11-08 13:21:35
321 2011-11-08 11:44:54
483 2011-11-08 11:32:00
483 2011-11-08 11:31:59
645 2011-11-04 10:03:15
645 2011-11-04 07:03:15
That is the result of the query to create #tmpTableResults
DECLARE #tmpTableResults TABLE
(
TmpInstallerID int,
TmpConfirmDate date,
TmpConfirmLocalTime time
)
DECLARE #tmpTableQuery VarChar(800)
SET #tmpTableQuery = 'select FxWorkorder.INSTALLERSYSID, FxWorkorder.CONFIRMDATE, FxWorkorder.CONFIRMLOCALTIME from FxWorkorder
join install on FxWorkorder.INSTALLERSYSID = install.sysid
join RouteGroupWorkarea on FxWorkorder.WORKAREAGROUPSYSID = RouteGroupWorkarea.IWORKAREA_ID
join RoutingGroup on RouteGroupWorkarea.IRG_ID = RoutingGroup.IRG_IDENTITY
where FxWorkorder.SCHEDULEDDATE > = #StartDate and FxWorkorder.SCHEDULEDDATE <= #EndDate
and FxWorkorder.Jobstatus <> "Unassign"
and FxWorkorder.Jobstatus <> "Route"
and install.FOXTELCODE <> ""
and FxWorkorder.CONFIRMLOCALTIME is not null
and FxWorkorder.CONFIRMDATE <> ""
group by FxWorkorder.INSTALLERSYSID, FxWorkorder.CONFIRMDATE, FxWorkorder.CONFIRMLOCALTIME
order by FxWorkorder.INSTALLERSYSID, FxWorkorder.CONFIRMDATE, FxWorkorder.CONFIRMLOCALTIME desc '
INSERT INTO #tmpTableResults EXEC(#tmpTableQuery)
I'm creating another query to get data from another table and only the first record from the temporal table for the same INSTALLERSYSID
SELECT RoutingGroup.SDESCRIPTION, FxWorkorder.INSTALLERSYSID, FxWorkOrder.JOBSTATUS, Install.FOXTELCODE,
install.NAME, FxWorkOrder.ScheduledDate,
count(*) as TotalJobs, COUNT(CONFIRMDATE) as ConfirmedJobs,
(select TmpInstallerID, TmpConfirmDate, TmpConfirmLocalTime from #tmpTableResults where TmpInstallerID = FxWorkorder.INSTALLERSYSID)
from FxWorkorder
join install on fxworkorder.INSTALLERSYSID = install.sysid
join RouteGroupWorkarea on FxWorkOrder.WORKAREAGROUPSYSID = RouteGroupWorkarea.IWORKAREA_ID
join RoutingGroup on RouteGroupWorkarea.IRG_ID = RoutingGroup.IRG_IDENTITY
where FxWorkorder.SCHEDULEDDATE > = #StartDate and FxWorkorder.SCHEDULEDDATE <= #EndDate
and FxWorkOrder.Jobstatus <> 'Unassign'
and FxWorkOrder.Jobstatus <> 'Route'
and Install.FOXTELCODE <> ''
group by RoutingGroup.SDESCRIPTION,FxWorkOrder.INSTALLERSYSID, FxWorkOrder.JOBSTATUS, Install.FOXTELCODE,install.NAME, FxWorkOrder.ScheduledDate,FxWorkOrder.WORKAREAGROUPSYSID
When I tried to save the sp I got the error
"Only one expression can be specified in the select list when the subquery is not introduced with EXISTS."
I can't see why I got this error. But if I run the query in sql that works. Can someone see the error?
I don't know how your second query works for you ‘in sql’ (where is that supposed to be? do you mean SSMS = SQL Server Management Studio?), but I'm sure it cannot possibly work in any version of SQL Server that exists at the moment. It's because of this subquery in the SELECT list:
(select TmpInstallerID, TmpConfirmDate, TmpConfirmLocalTime from #tmpTableResults where TmpInstallerID = FxWorkorder.INSTALLERSYSID)
The thing is, every expression in the SELECT clause should be scalar, but this subquery returns a row of more than one value. Even if it's only one row, it is illegal there, because it returns several columns. The subquery in that context should return no more than one value, i.e. it should be one column and the result produced should contain either no rows or just one.
You could try this query instead (although I'm not entirely sure without knowing more details about your schema):
SELECT
RoutingGroup.SDESCRIPTION,
FxWorkorder.INSTALLERSYSID,
FxWorkOrder.JOBSTATUS,
Install.FOXTELCODE,
install.NAME, FxWorkOrder.ScheduledDate,
count(*) as TotalJobs, COUNT(CONFIRMDATE) as ConfirmedJobs,
tmp.TmpInstallerID,
tmp.TmpConfirmDate,
tmp.TmpConfirmLocalTime
from FxWorkorder
join install on fxworkorder.INSTALLERSYSID = install.sysid
join RouteGroupWorkarea on FxWorkOrder.WORKAREAGROUPSYSID = RouteGroupWorkarea.IWORKAREA_ID
join RoutingGroup on RouteGroupWorkarea.IRG_ID = RoutingGroup.IRG_IDENTITY
join #tmpTableResults tmp ON tmp.TmpInstallerID = FxWorkorder.INSTALLERSYSID
where FxWorkorder.SCHEDULEDDATE > = #StartDate
and FxWorkorder.SCHEDULEDDATE <= #EndDate
and FxWorkOrder.Jobstatus <> 'Unassign'
and FxWorkOrder.Jobstatus <> 'Route'
and Install.FOXTELCODE <> ''
group by
RoutingGroup.SDESCRIPTION,
FxWorkOrder.INSTALLERSYSID,
FxWorkOrder.JOBSTATUS,
Install.FOXTELCODE,install.NAME,
FxWorkOrder.ScheduledDate,
FxWorkOrder.WORKAREAGROUPSYSID
tmp.TmpInstallerID,
tmp.TmpConfirmDate,
tmp.TmpConfirmLocalTime
That is, I added one more join, the one to #tmpTableResults, as well as added the columns you were trying to pull to the SELECT clause and to the GROUP BY clause.
Also, if I were you I would consider using short aliases for tables, like this:
SELECT
…
wo.INSTALLERSYSID,
wo.JOBSTATUS,
…
from FxWorkorder wo
join …
That might make your queries more readable.