Where is the "LEFT" operator in LINQ? - sql

Using SQL Server 2005 I've run a query like this
SELECT *
FROM mytable
WHERE (LEFT (title, 1) BETWEEN #PREFIXFROM AND #PREFIXTO)
I use this to do alphabet filtering, so for example
PREFIXFROM = a
PREFIXTO = c
and I get all the items in mytable between a and c (inclusive)
How do I do this in linq?
Selecting all the records fine.. but
1) how do I do the "LEFT" operation
and 2) How do I do a <= type operator with a non numeric field?
Any ideas appreciated!

Don't think of the SQL - think of what you're trying to achieve, and how you'd do it in .NET. So you're trying to get the first character, and work out whether it's between 'a' and 'c':
var query = from row in mytable
where row.title[0] >= 'a' && row.title[0] <= 'c'
select row;
or in dot notation:
var query = mytable.Where(row => row.title[0] >= 'a' && row.title[0] <= 'c');
Alternatively:
var query = mytable.Where(row => row.title.Substring(0, 1).CompareTo("a") >= 0 &&
row.title.Substring(0, 1).CompareTo("c") <= 0));

Related

LINQ query where column contains a specific number >= specific value you want to query

I have a table named SubcodeTable and I want to query in SubsidiaryCode where first 3characters is >= to 21Y
Query in table where a column contains >= '21Y'
SubcodeTable:
Column1 Column2
SubsidiaryCode Desc
18Y-001 AAA
19Y-001 AAA
20Y-001 AAA
21Y-001 CCC
22Y-003 EEE
23Y-001 FF
So output should display:
Column1 Column2
SubsidiaryCode Desc
21Y-001 CCC
22Y-003 EEE
23Y-001 FF
By the way first 3 characters of SubsidiaryCode represent year like 21Y=2021.
I manage to create a SQL query, so how do to it in LINQ query ?
SQL query:
SELECT LEN (Whh_SubsidiaryCode) [StringLength],LEFT(Whh_SubsidiaryCode,2)[FirsttwoCharInSubCode]
,Whd_WHNo [RR NUMBER], Whh_SubsidiaryCode [SubsidiaryCode], Whd_WHSeqNo [SEQ], Whd_WHSplitSeqNo [SPLIT SEQ] ,Whd_WHIssuedQty [QTY],Saw_StockName [ITEM NAME],Saw_StockSpecs1 [ASSET DESCRIPTION]
FROM E_WHDetailEntry
JOIN E_WHHeaderEntry ON Whd_WHNo=Whh_WHNo
JOIN E_StockAndWorkMaster ON Whd_StockCode=Saw_StockCode
WHERE Whd_StockType='FS2'
AND Whh_SubsidiaryCode LIKE '%Y%' AND LEFT(Whh_SubsidiaryCode,2) >= '21'
ORDER BY Whh_SubsidiaryCode
So this is my LINQ query, I tried to use y.Whh_SubsidiaryCode.Substring(0,2) >'20' but it says Too many characters in car literal & operator > cannot be applied to operands of type string and char.
private List<string> GetBudgetCodes()
{
using (var ctx = LinqExtensions.GetDataContext<NXpert.FixedAsset.DataAccess.FixedAssetDataContext>("AccountingDB"))
{
var list = (from x in ctx.DataContext.E_WHDetailEntries
join y in ctx.DataContext.E_WHHeaderEntries
on x.Whd_WHNo equals y.Whh_WHNo
where x.Whd_StockType == "FS2"
&& y.Whh_SubsidiaryCode.Contains("y")
&& y.Whh_SubsidiaryCode.Substring(0,2) >= '21'
select new { y.Whh_SubsidiaryCode }
).DistinctBy(y => y.Whh_SubsidiaryCode).OrderBy(y => y.Whh_SubsidiaryCode).ToList();
var budgetcode = list.Select(y => y.Whh_SubsidiaryCode.Trim()).ToList();
return budgetcode;
}
}
As it says in exception
> cannot be applied to operands of type string and char.
You need to parse Whh_SubsidiaryCode first to be able to compare values.
So this
&& y.Whh_SubsidiaryCode.Substring(0,2) >= '21'
should be like this:
&& Convert.ToInt32(y.Whh_SubsidiaryCode.Substring(0,2)) > 21
y.Whh_SubsidiaryCode.Substring(0,2) >'20' but it says
Too many characters in char literal
Indeed; you can only put one character inside ' in C#. If you want to put more than one character, you need a string, which is delimited by "
Operator > cannot be applied to operands of type string and char
> as a maths operation doesn't work with a string on one side and a char on the other. You can use it on two chars, or on two strings, but not on a mix
'a' > 'b' //false
"a" > "b" //false
"a" > "A" //true - a is ASCII 97, A is ASCII 65. 97 is greater than 65
I'd say you can simply use:
y.Whh_SubsidiaryCode >= "21Y"
if your code is always number number Y, but you should be aware that this is an alphameric comparison. It'll work fine for any other code that is also number number Y. Critically, a string of 100Y is not greater than 21Y because the first character, 1 is not greater than 2
Unless you have other codes like 21X, you can dump the LIKE '%Y%' / Contains("y") - that just slows things down
I finally gotten a solution in SQL.
by using LEN then get the length then using LEFT to get the first 2 char.
Then create a where clause for the condition:
LEFT(Whh_SubsidiaryCode,2) >= '20'
SELECT LEN (Whh_SubsidiaryCode) [StringLength],LEFT(Whh_SubsidiaryCode,2)[FirsttwoCharInSubCode]
,Whd_WHNo [RR NUMBER], Whh_SubsidiaryCode [SubsidiaryCode], Whd_WHSeqNo [SEQ], Whd_WHSplitSeqNo [SPLIT SEQ] ,Whd_WHIssuedQty [QTY],Saw_StockName [ITEM NAME],Saw_StockSpecs1 [ASSET DESCRIPTION]
FROM E_WHDetailEntry
JOIN E_WHHeaderEntry ON Whd_WHNo=Whh_WHNo
JOIN E_StockAndWorkMaster ON Whd_StockCode=Saw_StockCode
WHERE Whd_StockType='FS2'
AND Whh_SubsidiaryCode LIKE '%Y%' AND LEFT(Whh_SubsidiaryCode,2) >= '20'
ORDER BY Whh_SubsidiaryCode
Thanks for the help guys.
About the LINQ I manage to query the results using List, create a function that returns the met condition.
Code:
private static bool CodeFiltered(string code)
{
return ((Convert.ToInt32(code.Substring(0,2)) >= 20));
}
var lstBudgetCodes = GetBudgetCodes();
List<string> ResultCode =new List<string>(lstBudgetCodes.FindAll(CodeFiltered));

How to dynamic, handle nested WHERE AND/OR queries using Rails and SQL

I'm currently building a feature that requires me to loop over an hash, and for each key in the hash, dynamically modify an SQL query.
The actual SQL query should look something like this:
select * from space_dates d
inner join space_prices p on p.space_date_id = d.id
where d.space_id = ?
and d.date between ? and ?
and (
(p.price_type = 'monthly' and p.price_cents <> 9360) or
(p.price_type = 'daily' and p.price_cents <> 66198) or
(p.price_type = 'hourly' and p.price_cents <> 66198) # This part should be added in dynamically
)
The last and query is to be added dynamically, as you can see, I basically need only one of the conditions to be true but not all.
query = space.dates
.joins(:price)
.where('date between ? and ?', start_date, end_date)
# We are looping over the rails enum (hash) and getting the key for each key value pair, alongside the index
SpacePrice.price_types.each_with_index do |(price_type, _), index|
amount_cents = space.send("#{price_type}_price").price_cents
query = if index.positive? # It's not the first item so we want to chain it as an 'OR'
query.or(
space.dates
.joins(:price)
.where('space_prices.price_type = ?', price_type)
.where('space_prices.price_cents <> ?', amount_cents)
)
else
query # It's the first item, chain it as an and
.where('space_prices.price_type = ?', price_type)
.where('space_prices.price_cents <> ?', amount_cents)
end
end
The output of this in rails is:
SELECT "space_dates".* FROM "space_dates"
INNER JOIN "space_prices" ON "space_prices"."space_date_id" = "space_dates"."id"
WHERE "space_dates"."space_id" = $1 AND (
(
(date between '2020-06-11' and '2020-06-11') AND
(space_prices.price_type = 'hourly') AND (space_prices.price_cents <> 9360) OR
(space_prices.price_type = 'daily') AND (space_prices.price_cents <> 66198)) OR
(space_prices.price_type = 'monthly') AND (space_prices.price_cents <> 5500)
) LIMIT $2
Which isn't as expected. I need to wrap the last few lines in another set of round brackets in order to produce the same output. I'm not sure how to go about this using ActiveRecord.
It's not possible for me to use find_by_sql since this would be dynamically generated SQL too.
So, I managed to solve this in about an hour using Arel with rails
dt = SpaceDate.arel_table
pt = SpacePrice.arel_table
combined_clauses = SpacePrice.price_types.map do |price_type, _|
amount_cents = space.send("#{price_type}_price").price_cents
pt[:price_type]
.eq(price_type)
.and(pt[:price_cents].not_eq(amount_cents))
end.reduce(&:or)
space.dates
.joins(:price)
.where(dt[:date].between(start_date..end_date).and(combined_clauses))
end
And the SQL output is:
SELECT "space_dates".* FROM "space_dates"
INNER JOIN "space_prices" ON "space_prices"."space_date_id" = "space_dates"."id"
WHERE "space_dates"."space_id" = $1
AND "space_dates"."date" BETWEEN '2020-06-11' AND '2020-06-15'
AND (
("space_prices"."price_type" = 'hourly'
AND "space_prices"."price_cents" != 9360
OR "space_prices"."price_type" = 'daily'
AND "space_prices"."price_cents" != 66198)
OR "space_prices"."price_type" = 'monthly'
AND "space_prices"."price_cents" != 5500
) LIMIT $2
What I ended up doing was:
Creating an array of clauses based on the enum key and the price_cents
Reduced the clauses and joined them using or
Added this to the main query with an and operator and the combined_clauses

how do I join two tables sql

I have an issue that I'm hoping you can help me with. I am trying to create charting data for performance of an application that I am working on. The first step for me to perform two select statements with my feature turned off and on.
SELECT onSet.testName,
avg(onSet.elapsed) as avgOn,
0 as avgOff
FROM Results onSet
WHERE onSet.pll = 'On'
GROUP BY onSet.testName
union
SELECT offSet1.testName,
0 as avgOn,
avg(offSet1.elapsed) as avgOff
FROM Results offSet1
WHERE offSet1.pll = 'Off'
GROUP BY offSet1.testName
This gives me data that looks like this:
Add,0,11.4160277777777778
Add,11.413625,0
Delete,0,4.5245277777777778
Delete,4.0039861111111111,0
Evidently union is not the correct feature. Since the data needs to look like:
Add,11.413625,11.4160277777777778
Delete,4.0039861111111111,4.5245277777777778
I've been trying to get inner joins to work but I can't get the syntax to work.
Removing the union and trying to put this statement after the select statements also doesn't work. I evidently have the wrong syntax.
inner join xxx ON onSet.testName=offset1.testName
After getting the data to be like this I want to apply one last select statement that will subtract one column from another and give me the difference. So for me it's just one step at a time.
Thanks in advance.
-KAP
I think you can use a single query with conditional aggregation:
SELECT
testName,
AVG(CASE WHEN pll = 'On' THEN elapsed ELSE 0 END) AS avgOn,
AVG(CASE WHEN pll = 'Off' THEN elapsed ELSE 0 END) AS avgOff
FROM Results
GROUP BY testName
I just saw the filemaker tag and have no idea if this work there, but on MySQL I would try something along
SELECT testName, sum(if(pll = 'On',elapsed,0)) as sumOn,
sum(if(pll = 'On',1,0)) as numOn,
sum(if(pll ='Off',elapsed,0)) as sumOff,
sum(if(pll ='Off',1,0)) as numOff,
sumOn/numOn as avgOn,
sumOff/numOff as avgOff
FROM Results
WHERE pll = 'On' or pll='Off'
GROUP BY testName ;
If it works for you then this should be rather efficient as you do not need to join. If not, thumbs pressed that this triggers another idea.
The difficulty you have with the join you envisioned is that the filtering in the WHERE clause is performed after the join was completed. So, you would still not know what records to use to compute the averages. If the above is not implementable with FileMaker then check if nested queries work. You would then
SELECT testName, on.avg as avgOn, off.avg as avgOff
FROM ( SELECT ... FROM Results ...) as on, () as off
JOIN on.testName=off.testName
If that is also not possible then I would look for temporary tables.
OK guys... thanks for the help again. Here is the final answer. The statement below is FileMaker custom function that takes 4 arguments (platform, runID, model and user count. You can see the sql statement is specified. FileMaker executeSQL() function does not support nested select statements, does not support IF statements embedded in select statements (calc functions do of course) and finally does not support the SQL keyword VALUES. FileMaker does support the SQL keyword CASE which is a little more powerful but is a bit wordy. The select statement is in a variable named sql and result is placed in a variable named result. The ExecuteSQL() function works like a printf statement for param text so you can see the swaps do occur.
Let(
[
sql =
"SELECT testName, (sum( CASE WHEN PLL='On' THEN elapsed ELSE 0 END)) as sumOn,
sum( CASE WHEN PLL='On' THEN 1 ELSE 0 END) as countOn,
sum( CASE WHEN PLL='Off' THEN elapsed ELSE 0 END) as sumOff,
sum( CASE WHEN PLL='Off' THEN 1 ELSE 0 END) as countOff
FROM Results
WHERE Platform = ?
and RunID = ?
and Model = ?
and UserCnt = ?
GROUP BY testName";
result = ExecuteSQL ( sql ; "" ; ""
; platform
; runID
; model
; userCnt )
];
getAverages ( Result ; "" ; 2 )
)
For those interested the custom function looks like this:
getAverages( result, newList, pos )
Let (
[
curValues = Substitute( GetValue( data; pos ); ","; ¶ );
sumOn = GetValue( curValues; 2 ) ;
countOn = GetValue( curValues; 3 );
sumOff = GetValue( curValues; 4 );
countOff = GetValue( curValues; 5 );
avgOn = sumOn / countOn;
avgOff = sumOff / countOff
newItem = ((avgOff - avgOn) / avgOff ) * 100
];
newList & If ( pos > ValueCount( data); newList;
getAverages( data; If ( not IsEmpty( newList); ¶ ) & newItem; pos + 1 ))
)

how to convert a sql having sub query in the from clause into Linq?

I have a sql statement as below, I want to translate it into Linq
select *
from
(
select Top 12 *
from DailyData
where ddaCode = '600000' and ddaDate < '2008/12/31'
order by ddaDate desc) as X
order by ddaDate
How can I do it? Thank you.
The From subquery becomes your first Linq query. Then this is queried in the second one.
var fromResults = DailyData.Where(x => x.ddaCode == "600000"
&& x.ddaDate < new DateTime(2008,12,31)
.OrderByDescending(x => x.ddaDate)
.Take(12);
var results = fromResults.OrderBy(x => x.ddaDate)

Linq query output does not match with Sql query output

Repository.Calls
.Count(call => call.OutcomeActionDate.Value >= fourWeeksStartDate &&
call.OutcomeActionDate.Value < threeWeeksStartDate &&
call.UserId == user.UserId);
Above query gives me output 1 and the sql query:
select *
from calls
where userid = 1006 and
outcomeactiondate >= '2013-08-19' and
OutcomeActionDate < '2013-08-26'
gives me the output 15.
The output 15 is correct. I am not sure why is the linq query giving me incorrect value ?
All the parameter values used in select query are same as passed in the linq query.
Try to use Date part of filtered dates:
Calls.Count(call => call.OutcomeActionDate.Value >= fourWeeksStartDate.Date &&
call.OutcomeActionDate.Value < threeWeeksStartDate.Date &&
call.UserId == user.UserId);