How to generate better SQL from Linq2Sql query - sql

I have the following query:
var data = from d in dc.GAMEs
where (d.GAMEDATE + d.GAMETIME.Value.TimeOfDay) >= DateTime.Now select d;
This generates some horendous looking SQL, looking something like this:
SELECT {...} WHERE DATEADD(ms, ((CONVERT(BigInt,((CONVERT(BigInt,DATEPART(HOUR, [t0].[GAMETIME]))) * 36000000000) + ((CONVERT(BigInt,DATEPART(MINUTE, [t0].[GAMETIME]))) * 600000000) + ((CONVERT(BigInt,DATEPART(SECOND, [t0].[GAMETIME]))) * 10000000) + ((CONVERT(BigInt,DATEPART(MILLISECOND, [t0].[GAMETIME]))) * 10000))) / 10000) % 86400000, CONVERT(DateTime,DATEADD(day, (CONVERT(BigInt,((CONVERT(BigInt,DATEPART(HOUR, [t0].[GAMETIME]))) * 36000000000) + ((CONVERT(BigInt,DATEPART(MINUTE, [t0].[GAMETIME]))) * 600000000) + ((CONVERT(BigInt,DATEPART(SECOND, [t0].[GAMETIME]))) * 10000000) + ((CONVERT(BigInt,DATEPART(MILLISECOND, [t0].[GAMETIME]))) * 10000))) / 864000000000, [t0].[GAMEDATE]))) >= #p0
What is the reason for this massive amount of SQL? And is there a better way to deal with it?
EDIT:
I have no control over the schema. It is what it is, and I have to deal with it.

If you can change the schema then the best way to deal with it is to use a single datetime2 column instead of separate date and time fields. Your current query will not be able to use an index.
Otherwise you could try rewriting the query as follows:
DateTime now = DateTime.Now;
var data = from d in dc.GAMEs
where (d.GAMEDATE > now.Date) ||
(d.GAMEDATE == now.Date && d.GAMETIME.Value.TimeOfDay >= now.TimeOfDay)
select d;
The SQL generated by this query might be slightly more readable and perhaps also more efficient. On the other hand, from a programmer's perspective it is more important that the source code is readable than the generated SQL is readable. If performance is not a concern you may want to leave your code as it is and just accept that the generated SQL is ugly and not worry about it.

As an alternative (since outermost OR is the bane of indexes)
DateTime now = DateTime.Now;
DateTime today = now.Date;
TimeSpan timeOfDay = now.TimeOfDay;
var data =
from d in dc.GAMEs
where d.GAMEDATE >= today
&& (d.GAMEDATE > today || d.GAMETIME.Value.TimeOfDay >= timeOfDay)
select d;

Related

Need to convert a numeric field to date in this query

I am running this in sql server but there is an error on the date compare as the column cadodt is a numeric. Is there a quick and easy way to convert that col to numeric? It's date is in YYYYMMDD but I cannot compare against a date field like current date.
select * from openquery(IBSIBM, 'select CABANO, CABCRC, CABCTR, CABCUR, CABKCO, CACLVL, CACOTI,
CACRDT, CACTTP, CADODT, CADOTY, CAEXRT, CAIDNO, CAJONO, CANCRX,
CAPERI, CAREFX, CARSCD, CASCRC, CASCTR, CASTAT, CASTMT, CASYID,
CATIML, CATREF, CATTXT, CATYPC, CAUSER, CAVADT, CAVODT, CAVONO,
CAVOTY ,NANAME from CA1665AFCV.SROCBA,
CA1665AFCV.srolta,
CA1665AFCV.sronam
WHERE CADOTY = ''CHK'' and cajono=ctjono and cadodt = CURRENT DATE()
and caperi=ctperi and caidno=ctidno and ctveno=nanum
and CASTAT = ''Y''')
Assuming cadodt is actually an integer or numeric field and not a character string, try this:
select * from openquery(IBSIBM,
'select CABANO, CABCRC, CABCTR, CABCUR, CABKCO, CACLVL, CACOTI,
CACRDT, CACTTP, CADODT, CADOTY, CAEXRT, CAIDNO, CAJONO, CANCRX,
CAPERI, CAREFX, CARSCD, CASCRC, CASCTR, CASTAT, CASTMT, CASYID,
CATIML, CATREF, CATTXT, CATYPC, CAUSER, CAVADT, CAVODT, CAVONO,
CAVOTY, NANAME
from CA1665AFCV.SROCBA, CA1665AFCV.srolta, CA1665AFCV.sronam
WHERE CADOTY = ''CHK''
and cajono = ctjono
and cadodt = YEAR(CURRENT DATE) * 10000 + MONTH(CURRENT DATE) * 100 + DAY(CURRENT DATE)
and caperi = ctperi
and caidno = ctidno
and ctveno = nanum
and CASTAT = ''Y''')
Aside: I would strongly recommend against using comma joins. The longer ANSI 92 join syntax is much easier to read and maintain. I'd also strongly recommend using qualified column names. This schema is not the easiest to follow as it is. No need to make it even more arcane.
You have these options
convert cadodt from string to date
convert cadodt from intger to date
convert current date to YYYYMMDD string
convert current date to YYYYMMDD integer
1 and 3 assume cadodt is a string
2 and 4 assume cadodt is an intger
3 and 4 are the more efficient approaches as you only change one value.
e.g.
(3) if cadodt is a string in YYYMMDD format:
AND cadodt = VARCHAR_FORMAT(CURRENT DATE, 'YYYYMMDD')
(4) if cadodt is an integer representing YYYYMMDD
AND cadodt = YEAR(CURRENT DATE) * 10000 + MONTH(CURRENT DATE) * 100 + DAY(CURRENT DATE)
the CURRENT DATE() may be causing you some issues use CURRENT_DATE()
Try this:
select * from openquery(IBSIBM, 'select CABANO, CABCRC, CABCTR, CABCUR, CABKCO, CACLVL, CACOTI,
CACRDT, CACTTP, CADODT, CADOTY, CAEXRT, CAIDNO, CAJONO, CANCRX,
CAPERI, CAREFX, CARSCD, CASCRC, CASCTR, CASTAT, CASTMT, CASYID,
CATIML, CATREF, CATTXT, CATYPC, CAUSER, CAVADT, CAVODT, CAVONO,
CAVOTY ,NANAME from CA1665AFCV.SROCBA,
CA1665AFCV.srolta,
CA1665AFCV.sronam
WHERE CADOTY = ''CHK'' and cajono=ctjono and cadodt = CURRENT_DATE()
and caperi=ctperi and caidno=ctidno and ctveno=nanum
and CASTAT = ''Y''')
Replace CADODT = CURRENT DATE() with:
date(substr(char(CADODT), 1, 4) || '-'|| substr(char(CADODT),5, 2) || '-'|| substr(char(CADODT), 7, 2)) = CURRENT DATE
That should convert it from an integer to a date.

Calculate value per day

I have Oracle table where I insert data about network upload and download speed.
CREATE TABLE AGENT_HISTORY(
EVENT_DATE DATE,
NETWORK_UP NUMBER,
NETWORK_DOWN NUMBER
)
I want to generate Bar chart for last 30 days and display total upload traffic per day(24 hours).
select * from AGENT_HISTORY where EVENT_DATE >= SYSDATE - 30;
The problem which I don't know how to solve is how I can calculate the traffic for each day from the column NETWORK_UP. The result of the query should be 30 days with total upload traffic for each day. Is this possible without PL/SQL procedure?
You can do a query like this to aggregate totals data for both the network_up and network_down columns per day.
select trunc(event_day,'day') event_date
,sum(network_up) tot_network_up
,sum(network_down) tot_network_down
from agent_history
where event_day >= trunc(sysdate,'day') - interval '30' day
group by trunc(event_day,'day');
You can do it without embbeding the query in PL/SQL stored code depending on what you use for front end, in java you could you use something like this
Statement stmt = null;
String schema_name = 'abc';
String query = "select trunc(event_day,'day') event_date," +
"sum(network_up) tot_network_up," +
"sum(network_down) tot_network_down " +
"from " + schema_name +".agent_history " +
"where event_day >= trunc(sysdate,'day') " +
"- interval '30' day " +
"group by trunc(event_day,'day')"
try {
stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
String event_data = rs.getString("EVENT_DATE");
int tot_network_up = rs.getInt("TOT_NETWORK_UP");
int tot_network_down= rs.getInt("TOT_NETWORK_DOWN");
.....
}
catch (SQLException e) {
.......
} finally {
......
}
With something like this you just execute pure SQL.
My guess is that you just want to aggregate the data. Something like
SELECT trunc(event_date),
sum(network_up) total_up,
sum(network_down) total_down
FROM agent_history
WHERE event_date >= trunc(sysdate) - 30
GROUP BY trunc(event_date)
If that is not what you want, it would be very helpful to post some sample data, expected output, etc.

How to write this query compatible with oracle database

I am facing issue to convert the sql query to oracle .Actually i am new to oracle db.
SELECT TI1.FOLDERRSN, DBO.F_OPENTAX_PROPERTYROLL_FOLDER(TI1.FOLDERRSN) ROLL,
TI1.DUEDATE DUEDATE, TI1.YEARFORBILLING,(TI1.SUMDUETAX + TI1.SUMPAIDTAX + TI1.SUMDUEPENALTY + TI1.SUMPAIDPENALTY) OUTSTANDING
FROM TAXINSTALLMENT TI1 WHERE (TI1.SUMDUETAX + TI1.SUMPAIDTAX + TI1.SUMDUEPENALTY + TI1.SUMPAIDPENALTY) > 0
AND EXISTS (SELECT * FROM TAXINSTALLMENT TI2 WHERE YEAR(TI2.DUEDATE) BETWEEN 1980 AND YEAR(GETDATE()) - 5 AND (TI2.SUMDUETAX + TI2.SUMPAIDTAX + TI2.SUMDUEPENALTY + TI2.SUMPAIDPENALTY) > 0
AND TI2.FOLDERRSN = TI1.FOLDERRSN ) ORDER BY TI1.FOLDERRSN, DUEDATE DESC
Anyone give me idea to change to oracle .
Thanks
The statement uses a function F_OPENTAX_PROPERTYROLL_FOLDER(TI1.FOLDERRSN) in Schema DBO. Make sure you have installed that function in that database Schema and have granted an execution right to the user in question or to PUBLIC for that matter.
EDIT: Moreover there is no function YEAR in Oracle. You must replace it with EXTRACT: EXTRACT(YEAR FROM TI2.DUEDATE) and for GetDate(): EXTRACT(YEAR FROM SYSDATE).

Extra parenthesis changing result of formula in SQL Server 2008

In all other languages (arithmetic engines in general) putting an extra set of parenthesis around operators of same priority does not impact results. But recently in a testing project I noticed that MS SQL server changes the results in those cases. Please take a look at the query below, and let me know if you have any idea (or a setting in SQL Server administration) or any links to MSDN article explaining the behavior.
select (0.55 * 287.61 / 0.66) calc_no_parens
,(0.55 * (287.61 / 0.66)) calc_parens
,round(0.55 * 287.61 / 0.66,2) no_paren_round
,round(0.55 * (287.61 / 0.66),2) paren_round;
Results
Column Record 1
calc_no_parens 239.6750000
calc_parens 239.67499985
no_paren_round 239.6800000
paren_round 239.67000000
To me, first two of them should return 239.675, and round should give 239.68.
You will get the desired result if you declare each value as Float.
DECLARE #Float1 float, #Float2 float, #Float3 float;
SET #Float1 = 0.55;
SET #Float2 = 287.61;
SET #Float3 = 0.66;
select (#Float1 * #Float2 / #Float3) calc_no_parens
,(#Float1* (#Float2/ #Float3)) calc_parens
,round(#Float1 * #Float2/ #Float3,2) no_paren_round
,round(#Float1* (#Float2/ #Float3),2) paren_round;
Output
calc_no_parens calc_parens no_paren_round paren_round
239.675 239.675 239.68 239.68
You may want to see this article: So-called "exact" numerics are not at all exact!
I can see what is happening, but I don't think there is a fix.
SQL calculates and stores each part of the function as a SQL data type (in this case it's a floating point number).
287.61/0.66 produces 435.7727272727272727272727272... which SQL will store as a floating point number to some degree of accuracy, however it isn't exact (after all, it's a floating point number).
For more info on floating point numbers: How is floating point stored? When does it matter?
Habib's answer made me thinking this has to be with decimal data types my columns are using. After a bit of research, I found this
Precision, Scale, and Length (Transact-SQL)
As you can see in that article, division operation significantly changes the both scale and precision of resulting decimal. Then I tried an variation of my query, this time adding extra parenthesis around Multiplication operation.
select distinct (0.55 * 287.61 / 0.66) calc_no_parens
,(0.55 * (287.61 / 0.66)) calc_parens_div
,((0.55 * 287.61) / 0.66) calc_parens_mult
,round(0.55 * 287.61 / 0.66,2) no_paren_round
,round(0.55 * (287.61 / 0.66),2) paren_round
,round((0.55 * 287.61) / 0.66,2) paren_round2;
Results
Column Record 1
calc_no_parens 239.6750000
calc_parens_div 239.67499985
calc_parens_mult 239.6750000
no_paren_round 239.6800000
paren_round 239.67000000
paren_round2 239.6800000
So as long as division is the last operator in the formula we get correct answers. Its not a fix to the problem, but a learning to self in any future testing projects.
When you use numbers SQL try to convert them dynamically:
{
SELECT
0.55*(287.61 / 0.66) PrecisionError,
0.55* (CONVERT(NUMERIC(24,12), 287.61) / CONVERT(NUMERIC(24,12), 0.66)) NotPrecisionError
DECLARE #V SQL_VARIANT
SET #V = 0.55*(287.61 / 0.66)
SELECT
Value = #V
,[TYPE] = CONVERT(SYSNAME, sql_variant_property(#V, 'BaseType')) + '(' +
CONVERT(VARCHAR(10), sql_variant_property(#V, 'Precision')) + ',' +
CONVERT(VARCHAR(10), sql_variant_property(#V, 'Scale')) + ')'
SET #V = 0.55 * (CONVERT(NUMERIC(24,14), 287.61) / CONVERT(NUMERIC(24,14), 0.66))
SELECT
Value = #V
,[TYPE] = CONVERT(SYSNAME, sql_variant_property(#V, 'BaseType')) + '(' +
CONVERT(VARCHAR(10), sql_variant_property(#V, 'Precision')) + ',' +
CONVERT(VARCHAR(10), sql_variant_property(#V, 'Scale')) + ')'
}
RESULTS
PrecisionError NotPrecisionError
239.67499985 239.6750000000000
Value TYPE
239.67499985 numeric(14,8)
Value TYPE
239.6750000000000 numeric(38,13)

Add a random number between 30 and 300 to an existing field

I have looked on the net as well as here but can't find an answer to the following MySQL question. I'm looking to replace the value of an existing field with a query that has a random number between 30 and 300.
Reason was because I've moved galleries and had 250,000,000 views on my images and there have been lost with the migration and a lot of my members are upset that they have lost views....
UPDATE the_table SET the_field = the_field + FLOOR(RAND() * (270 + 1)) + 30
Use RAND()
UPDATE table
SET field = FLOOR(30 + (RAND() * 270));
WHERE foo = 'bar'
I think this will do the trick:
UPDATE table SET field = ROUND(30 + (RAND() * 270)) WHERE id =1;