Could you please optimize this query for me:
SELECT
case when EXISTS (
SELECT
1
FROM
usergroups item_t1
WHERE
(item_t0.p_b2bunits = item_t1.p_uid)
AND (item_t1.TypePkString = 8796095217746)
) THEN true ELSE false END as active,
item_t0.p_b2bunits as code,
item_t0.p_accountname as name1,
item_t0.p_accountnameextension as name2,
case when EXISTS (
SELECT
1
FROM
user2salreprelation item_t2
WHERE
(
item_t2.SourcePK = item_t0.PK
and item_t2.TargetPK = 8828959653892
)
AND (item_t2.TypePkString = 8796150399058)
) THEN true ELSE false END as isFav
FROM
account2salesrep item_t0
WHERE
(
item_t0.p_showondashboard = 1
AND item_t0.p_district = '4'
AND (
LOWER(item_t0.p_accountname) LIKE CONCAT('%', CONCAT('crampel alu', '%'))
OR LOWER(item_t0.p_accountnameextension) LIKE CONCAT('%', CONCAT('crampel alu', '%'))
OR LOWER(item_t0.p_b2bunits) LIKE CONCAT('%', CONCAT('crampel alu', '%'))
)
)
AND (item_t0.TypePkString = 8796149842002)
order by
item_t0.p_accountname
LIMIT
0, 11;
Typing If TypePkString is a VARCHAR, then you must quote 8796150399058, else performance will be really bad. (The opposite situation does not matter.)
true/false A boolean expression (such as EXISTS(...)) return 0 for false and 1 true, so this
CASE WHEN (...) THEN true ELSE false AS ...
can be simplified
(...) AS ...
Some of these indexes may help:
item_t0: INDEX(p_showondashboard, p_district, TypePkString, p_accountname)
item_t1: INDEX(p_uid, TypePkString)
item_t2: INDEX(SourcePK, TargetPK, TypePkString)
Unnecessary LOWER Assuming that p_accountname has a collation ending in _ci [Please provide SHOW CREATE TABLE], you can remove LOWER() as being unnecessary and slow.
LIKE with a leading wildcard (%) defies indexing. Have you considered using FULLTEXT (if the users enter "words", not random strings). It would involve
FULLTEXT(p_accountname, p_accountnameextension, p_accountnameextension)
MATCH(p_accountname, p_accountnameextension, p_accountnameextension)
AGAINST('+crampel alu' IN BOOLEAN MODE)
Is crampel alu a column name? A prefix? Something else?
Related
I have several stored procedures that are almost identical but have some different AND parts inside a WHERE clause.
Based on a variable deptname, I want to add additional AND/OR conditions to my already existing WHERE clause. So kind of like IF/CASE WHEN on the part that is different.
Think about it as string concatenation
query_string = 'WHERE a= XYZ AND B= 123"
if deptname = a: query_string + "AND additional conditions for dept a"
else if deptname = b:query_string + "AND additional conditions for dept b"
What is the appropriate way to use a variable?
here is some pseudo code of what I am trying to do
SELECT
personID AS pid,
personcode,
persondeptcode,
more_fields AS fields
FROM
TABLE_XYZ
WHERE
--shared parts
personcode = 'C'
AND
persondeptcode = 'MAJ'
--- NOW the different part
IF #deptname = "deptA"
AND
(
PROGRAM_LDESCR IN
(
'prog1',
'prog2',
'prog3'
)
OR
aprogram IN ('aprogram1')
OR
(aprogram IN ('aprogram2') AND PLAN_LDESCR IN ('plan123'))
);
--- THIS IS A DIFFERENT DEPT SO WE HAVE DIFFERENT AND PART
ELSE IF #deptname = "deptB"
(
PROGRAM_LDESCR IN
(
'1234'
)
OR
aprogram IN ('a1234')
);
You can use a CASE expression in this case, the important thing is to make sure you have an ELSE clause to ensure the expression remains true if #deptname is not one of the two values with extra conditions:
WHERE personcode = 'C'
AND persondeptcode = 'MAJ'
AND (CASE #deptname
WHEN "deptA" THEN PROGRAM_LDESCR IN ('prog1', 'prog2', 'prog3')
OR aprogram IN ('aprogram1')
OR aprogram IN ('aprogram2') AND PLAN_LDESCR IN ('plan123')
WHEN "deptB" THEN PROGRAM_LDESCR IN ('1234')
OR aprogram IN ('a1234')
ELSE 1
END)
Here is a simple demo of a CASE expression used in this fashion.
You seem to want something like:
AND
(#deptname = 'dept123' AND (PROGRAM_LDESCR IN ('1234') OR aprogram IN ('a1234')) OR
#deptname <> 'dept123'
)
To combine the last part of the WHERE clause (if I'm understanding your commented-code correctly), you could do something like the following:
SELECT
personID AS pid,
personcode,
persondeptcode,
more_fields AS fields
FROM
TABLE_XYZ
WHERE
personcode = 'C'
AND persondeptcode = 'MAJ'
AND (
(#deptname="deptA" AND (PROGRAM_LDESCR IN ('prog1', 'prog2', 'prog3') OR aprogram IN ('aprogram1') OR (aprogram IN ('aprogram2') AND PLAN_LDESCR IN ('plan123'))))
OR
(#deptname="deptB" AND (PROGRAM_LDESCR IN ('1234') OR aprogram IN ('a1234'))
)
Normally you would use the WHERE clause to filter out unnecessary rows of data and a CASE statement if you wanted to actually change the value in the SELECT statement (I rarely see CASE statements outside a SELECT clause, unless it is doing something like a complex sort).
In the existing statement below I am comparing the host_name and ip_adress with records retrieved from an other table and mark the column as true when found, but I would like to tweak it to give a true result when the host_name is also a part of identifer from the record retrieved
i.e
host_name=abc
identifier=abc.google.com should also be true
CASE
WHEN
(
(`inv_y`.`host_name` <> '')
AND
`inv_y`.`host_name` IN (SELECT `inv_x`.`identifier`
FROM `inv_x`
ORDER BY `inv_x`.`creation_date` DESC
)
)
OR
(
(`inv_y`.`ip_address` <> '')
AND
inv_y`.`ip_address` IN (SELECT `inv_x`.`identifier`
FROM `inv_x`
ORDER BY `inv_x`.`creation_date` DESC
)
)
THEN
'True'
ELSE
'False'
END
It looks like you could benefit from the EXISTS function. Something like the following could work:
SELECT CASE WHEN EXISTS(SELECT 1
FROM inv_y
WHERE (ip IN (SELECT identifier
FROM inv_x)
OR hostname IN (SELECT identifier
FROM inv_x))
AND hostname LIKE '%eb%'
) = 1 THEN "True"
ELSE "False"
END AS Present;
Here's a demo of this working: SQL Fiddle
Note, the above could be simplified further if you can work with a 1 or 0 instead of true or false. EXISTS returns a 1 if the row is found, and 0 if not, and the CASE is there simply to translate this into 'True' or 'False', so you could simply have the following:
SELECT EXISTS(SELECT 1
FROM inv_y
WHERE (ip IN (SELECT identifier
FROM inv_x)
OR hostname IN (SELECT identifier
FROM inv_x)
)
AND hostname LIKE '%eb%') AS Present;
I have a table with 3 columns( name, objectroot_dn, distinguishedname). Here distinguishedname is like a parent to objectroot_dn. I have to find whether for each objectroot_dn is there a child exists or not?
I can do this using the query below. It will return True if there is a child, False if there is not. But my problem is when the total dataset gets increased it takes lots of time.
For example, If the total number of row is 50,000 then it takes 10 mins for this query to complete.
Since I'm using a framework for different database, I can't index the columns.
SELECT
name,
objectroot_dn,
distinguishedname,
CASE
WHEN (SELECT count(*)
FROM (SELECT name
FROM elaoucontainergeneraldetails
WHERE objectroot_dn = dn.distinguishedname
LIMIT 1) AS tabel1) > 0
THEN 'True'
ELSE 'False'
END
FROM elaoucontainergeneraldetails AS dn
WHERE objectroot_dn = 'SOME_VALUE';
Please let me know how can I increase the speed of this query.
Thanks in advance. Appreciate all help.
You can have the same solution using left join or exists:
SELECT
dn.name,
dn.objectroot_dn,
dn.distinguishedname,
CASE
WHEN dn_in.objectroot_dn is not null
THEN 'True'
ELSE 'False'
END
FROM elaoucontainergeneraldetails AS dn
LEFT JOIN elaoucontainergeneraldetails dn_in on dn_in.objectroot_dn = dn.distinguishedname
WHERE objectroot_dn = 'SOME_VALUE';
EXISTS(subquery) yields a boolean value:
SELECT dn.name
, dn.objectroot_dn
, dn.distinguishedname
, EXISTS (SELECT *
FROM elaoucontainergeneraldetails nx
WHERE nx.objectroot_dn = dn.distinguishedname
) AS truth_value
FROM elaoucontainergeneraldetails AS dn
WHERE dn.objectroot_dn = 'SOME_VALUE'
;
I would like to use if statement in sql query :
what I want :
if(tractions_delivery.send_date_id !=0 ){
date_send_commodities.id = tractions_delivery.send_date_id
}
my query :
from
tractions_delivery,user_address,province,city,date_send_commodities,users
WHERE
tractions_delivery.tr_id = $tr_id
AND
tractions_delivery.address_id = user_address.id
AND
user_address.province_id = province.id
AND
user_address.city_id = city.id
AND
//not work
(tractions_delivery.send_date_id IS NOT 0 date_send_commodities.id = tractions_delivery.send_date_id)
AND
users.id = user_address.user_id
You could use the CASE-statement
SELECT
*
FROM
tractions_delivery,
user_address,
province,
city,
date_send_commodities,users
WHERE
tractions_delivery.tr_id = $tr_id AND
tractions_delivery.address_id = user_address.id AND
user_address.province_id = province.id AND
user_address.city_id = city.id AND
CASE WHEN tractions_delivery.send_date_id != 0 THEN date_send_commodities.id = tractions_delivery.send_date_id ELSE 1=1 END AND
users.id = user_address.user_id
You can only use if statements in stored procedures or functions. If you just write a sql statement unfortunately you cannot use if statements around the query. But you can use logic in the query itself, e.g.:
SELECT CASE WHEN col1 = col2 THEN'col1 equals col2' else 'col1 doesnt equal col2' ELSE
FROM table1
So around doesnt work, but in the field list you can create CASE WHEN ELSE END logic.
CASE or IF() operators can be of help.
Examples,
SELECT (CASE 1 WHEN 1 THEN 'One' WHEN 2 THEN 'Two' ELSE 'More' END) 'Result';
OR
SELECT IF(1=1, 'One', 'Two') 'Result';
These CASE and IF() operators can be used in the SELECT clause to conditionally interpret column values and return in the resultset.
Note: Do not confuse CASE operator here with 'CASE conditional syntax block' that ends with END CASE.
I have a function (called "powersearch", the irony!) that searches for a set of strings across a bunch(~ 5) of fields.
The words come in as one string and are separated by spaces.
Some fields can have exact matches, others should have "contains".
(Snipped for brevety)
//Start with all colors
IQueryable<Color> q = db.Colors;
//Filter by powersearch
if (!string.IsNullOrEmpty(searchBag.PowerSearchKeys)){
foreach (string key in searchBag.SplitSearchKeys(searchBag.PowerSearchKeys)
.Where(k=> !string.IsNullOrEmpty(k))){
//Make a local copy of the var, otherwise it gets overwritten
string myKey = key;
int year;
if (int.TryParse(myKey, out year) && year > 999){
q = q.Where(c => c.Company.Name.Contains(myKey)
|| c.StockCode.Contains(myKey)
|| c.PaintCodes.Any(p => p.Code.Equals(myKey))
|| c.Names.Any(n => n.Label.Contains(myKey))
|| c.Company.CompanyModels.Any(m => m.Model.Name.Contains(myKey))
|| c.UseYears.Any(y => y.Year.Equals(year))
);
}
else{
q = q.Where(c => c.Company.Name.Contains(myKey)
|| c.StockCode.Contains(myKey)
|| c.PaintCodes.Any(p => p.Code.Contains(myKey))
|| c.Names.Any(n => n.Label.Contains(myKey))
|| c.Company.CompanyModels.Any(m => m.Model.Name.Equals(myKey))
);
}
}
}
Because the useYear count is rather large, I tried to check for it as little as possible by outruling all numbers that can never be a number that makes sence in this case. Similar checks are not possible on the other fields since they can pretty much contain any thinkable string.
Currently this query takes about 15 secs for a single, non-year string. That's too much.
Anything I can do to improve this?
--Edit--
Profiler shows me the following info for the part where the string is not a year:
exec sp_reset_connection
Audit login
exec sp_executesql N'
SELECT COUNT(*) AS [value]
FROM [dbo].[CLR] AS [t0]
INNER JOIN [dbo].[CO] AS [t1] ON [t1].[CO_ID] = [t0].[CO_ID]
WHERE
([t1].[LONG_NM] LIKE #p0)
OR ([t0].[EUR_STK_CD] LIKE #p1)
OR (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[PAINT_CD] AS [t2]
WHERE ([t2].[PAINT_CD] LIKE #p2)
AND ([t2].[CLR_ID] = [t0].[CLR_ID])
AND ([t2].[CUSTOM_ID] = [t0].[CUSTOM_ID])
)
)OR (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[CLR_NM] AS [t3]
WHERE ([t3].[CLR_NM] LIKE #p3)
AND ([t3].[CLR_ID] = [t0].[CLR_ID])
AND ([t3].[CUSTOM_ID] = [t0].[CUSTOM_ID])
)
) OR (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[CO_MODL] AS [t4]
INNER JOIN [dbo].[MODL] AS [t5] ON [t5].[MODL_ID] = [t4].[MODL_ID]
WHERE ([t5].[MODL_NM] = #p4)
AND ([t4].[CO_ID] = [t1].[CO_ID])
)
)
',N'#p0 varchar(10),#p1 varchar(10),#p2 varchar(10),#p3 varchar(10),#p4 varchar(8)',#p0='%mercedes%',#p1='%mercedes%',#p2='%mercedes%',#p3='%mercedes%',#p4='mercedes'
(took 3626 msecs)
Audit Logout (3673 msecs)
exec sp_reset_connection (0msecs)
Audit login
exec sp_executesql N'
SELECT TOP (30)
[t0].[CLR_ID] AS [Id],
[t0].[CUSTOM_ID] AS [CustomId],
[t0].[CO_ID] AS [CompanyId],
[t0].[EUR_STK_CD] AS [StockCode],
[t0].[SPCL_USE_CD] AS [UseCode],
[t0].[EFF_IND] AS [EffectIndicator]
FROM [dbo].[CLR] AS [t0]
INNER JOIN [dbo].[CO] AS [t1] ON [t1].[CO_ID] = [t0].[CO_ID]
WHERE
([t1].[LONG_NM] LIKE #p0)
OR ([t0].[EUR_STK_CD] LIKE #p1)
OR (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[PAINT_CD] AS [t2]
WHERE ([t2].[PAINT_CD] LIKE #p2)
AND ([t2].[CLR_ID] = [t0].[CLR_ID])
AND ([t2].[CUSTOM_ID] = [t0].[CUSTOM_ID])
)
)
OR (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[CLR_NM] AS [t3]
WHERE ([t3].[CLR_NM] LIKE #p3)
AND ([t3].[CLR_ID] = [t0].[CLR_ID])
AND ([t3].[CUSTOM_ID] = [t0].[CUSTOM_ID])
)
)
OR (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[CO_MODL] AS [t4]
INNER JOIN [dbo].[MODL] AS [t5] ON [t5].[MODL_ID] = [t4].[MODL_ID]
WHERE ([t5].[MODL_NM] = #p4)
AND ([t4].[CO_ID] = [t1].[CO_ID])
)
)'
,N'#p0 varchar(10),#p1 varchar(10),#p2 varchar(10),#p3 varchar(10),#p4 varchar(8)',#p0='%mercedes%',#p1='%mercedes%',#p2='%mercedes%',#p3='%mercedes%',#p4='mercedes'
(took 3368 msecs)
The database structure, sadly, is not under my control. It comes from the US and has to stay in the exact same format for compatibility reasons. Although most of the important fields are indeed indexed, they are indexed in (unnecessary) clustered primary keys. There's verry little I can do about that.
Okay, let's break this down - the test case you're interested in first is a single non-year, so all we've got is this:
q = q.Where(c => c.Company.Name.Contains(myKey)
|| c.StockCode.Contains(myKey)
|| c.PaintCodes.Any(p => p.Code.Contains(myKey))
|| c.Names.Any(n => n.Label.Contains(myKey))
|| c.Company.CompanyModels.Any(m => m.Model.Name.Equals(myKey))
Am I right? If so, what does the SQL look like? How long does it take just to execute the SQL statement in SQL Profiler? What does the profiler say the execution plan looks like? Have you got indexes on all of the appropriate columns?
Use compiled queries.
If you don't, you will lose up to 5-10x times performance, as LINQ-to-SQL will have to generate SQL from query every time you call it.
Things become worse when you use non-constants in LINQ-to-SQL as getting their values is really slow.
This assumes that you already have indexes and sane DB schema.
BTW, I am not kidding about 5-10x part.