What is the SQL Server "datepart", syntactically? - sql-server

There's something about the SQL Server time functions that I haven't noticed anywhere else in the language.
For example, let's look at the function DATEADD():
DECLARE a_date datetime = '2006-08-31';
DATEADD(month, 1, a_date);
The 2nd and 3rd arguments are both expressions which evaluate to a value whose type I understand (int and datetime, in this case). But what exactly is the first argument? Is month a pre-defined global variable of some type? Is it a literal? Or a keyword? But it doesn't seem that every valid datepart is in the list of reserved words. Or is it some special syntactical category? If so, are there other examples of that category in the language? It doesn't seem I can treat it the same way as another variable or literal (SELECT month gives an error, for example).
Sorry if this is off topic for this site, but I wasn't sure which one it fit best under.

Related

How do I reference a field from a repeatable instrument in REDCap?

I'm trying to use datediff() to calculate age in a longitudinal REDCap database, but the function is returning [no value], despite the calculation being valid and the smart variable help page corroborating that the function seems correct.
The first date is in a non-repeating instrument in one event. The second date, and also where the calculation is being done, is in a field in a second, repeatable instrument, in a separate, non-repeatable event.
My calculation currently looks like this:
datediff([firstdate],[seconddate][current-instance], "y")
I've also (for lack of any idea how to fix it), tried
datediff([firstdate],[secondeventname][seconddate], "y")
Both calculations return [no value]. I've double checked that the dates are in the same ymd format, and that the function DOES work when I replace the second argument with 'today', so I know that the issue is the second argument, but the smart variable FAQ seems to be suggesting the first line of code above, which of course hasn't been working.
Does anyone have experience with what the issue might be?
In a longitudinal data collection project, you should prefix your variables with the event that it comes from, otherwise REDCap will only look into the current event for that variable, and return no value if it can't find anything.
Furthermore, the datediff function takes a 4th parameter for the date format, either "ymd", "dmy" or "mdy", and both date1 and date2 must be in the same format.
You may not need the smart variable for current-instance, at least in my testing for this I didn't need it, since if you are performing this calculation from the event that contains [seconddate], indeed from the instance if it is repeating, then you might only need to use [seconddate] to reference it, whereas to reference [firstdate] you need to prefix it with [event_1_arm_1] or whatever your event name is, or the smart variable [first-event-name] (which would be much more portable for multi-arm studies).
So I would try the following:
datediff( [first-event-name][firstdate], [seconddate], "y", "ymd" )

SQL Server user defined function with fixed parameter

Is it possible in SQL Server to define a user defined function with fixed enumerable parameters?
Like many pre-defined functions in SQL Server like DATEDIFF that takes DAY, MONTH, etc as first parameter, but these are not char, or any other data types...
I think that it should be easy to find the answer on Internet, but I don't know what I should exactly search. 😅😅
SQL Server doesn't have constants or enums in that sense; parameters to functions or procedures require to pass in strings, numbers, or variables.
Yes, this is unlike the ability to use well-defined constants directly in built-in functions and other T-SQL structs. Maybe that's something we'll see in a future evolution of the language, but this is what we have today.
For things like DATEADD you are passing an identifier... note that these work:
SELECT DATEADD([DAY], 1, GETDATE());
SELECT DATEADD("DAY", 1, GETDATE());
But this will fail:
SELECT DATEADD('DAY', 1, GETDATE());
Interestingly this will also fail (just further evidence that this is being handled like an identifier):
SET QUOTED_IDENTIFIER OFF;
SELECT DATEADD("DAY", 1, GETDATE());
You can't write your own functions or procedures that take identifiers as input - they are always either interpreted as an implicitly converted string (as in EXEC sp_who active;) or they simply fail a parse check (as in the above). Input parameters to built-in and user-defined functions will take expressions, but procedures will not.

SQL would using between statement improve this?

I want to find out using a select statement what columns in a table share similar information.
Example: Classes table with ClassID, ClassName, ClassCode, ClassDescription columns.
This was part of my SQL class that I already turned in. The question asked "What classes are part of the English department?"
I used this Select statement:
SELECT *
FROM Classes
WHERE ClassName LIKE "English%" OR ClassCode LIKE "ENG%"
Granted we have only input one actual English course in this database, the end result was it executed fine and displayed everything for just the English class. Which I thought was a success since we did populate other non English courses in the database.
Anyways, I was told I should have used a BETWEEN statement.
I am just sitting here thinking they would both do what I needed them to do right?
I'm using SQL Server 2014
No, BETWEEN would probably be a bad idea here. BETWEEN doesn't allow wildcards and doesn't do any pattern matching in any RDBMS I've used. So you'd have to say BETWEEN 'ENG' AND 'English'. Except that doesn't return things like 'English I' (which would be after 'English' in a sorted list).
It would also potentially include something like 'Engineering' or 'Engaging Artistry', but that's a weakness of your existing query, too, since LIKE 'ENG%' matches those.
If you happen to be using a case-sensitive collation you add a whole new dimension of complexity. Your BETWEEN statement gets even more confusing. Just know that capital letters generally come before lower case letters, so 'ENGRAVING I' would be included but 'Engraving I' would not. Additionally, 'eng' would not be included. Note that case-insensitive collation is the default.
Also whats the difference when searching for null values in one table
and one column
column_name =''
or
column_name IS NULL
You're not understanding the difference between an empty string and null.
An empty string is explicit. It says "This field has a known value and it is a string of zero length."
A null string is imprecise. It means "unknown". It could mean "This value wasn't asked for," or "This value was not available," or "This value has not yet been determined," or "This values does not make sense for this record."
"What is this person's middle name?"
"He doesn't have one. See, his birth certificate has no middle name listed." --> Empty string
"I don't know. He never told me and I don't have any birth or identity record." --> NULL
Note that Oracle, due to backwards compatibility, treats empty strings as NULLs. This is explicitly against ANSI SQL, but since Oracle is that old and that's how it's always worked that's how it will continue to work.
Another way to look at it is the example I tend to use with numbers. The difference between 0 and NULL is the difference between having a bank account with $0 balance and not having a bank account at all.
Nothing can be said unless we see table and its data.Though don't use between.
Secondly first find which of the column is not null by design.Say for example ClassName cannot be null then there is no use using ClassCode LIKE "ENG%",just ClassName LIKE "English%" is enough,similarly vice versa is also true.
Thirdly you should use same parameter in both column.for example
ClassName LIKE "English%" OR ClassCode LIKE "English%"
see the difference.
Select * FROM Classes
Where ClassName LIKE "%English%"

What is the SQLite command for getting the data between a start and end time?

Let's say my database is called "data" and its second column is called "datetime". The format of my datetime is "7/22/2011 12:00:00". If I want to get the the information between 12:00 and 13:00 today, how do I do that?
SELECT * FROM data WHERE
strftime('%m/%d/%Y %H:%M:%S', datetime) > strftime('%m/%d/%Y %H:%M:%S', '7/22/2011 12:00:00')
AND
strftime('%m/%d/%Y %H:%M:%S', datetime) < strftime('%m/%d/%Y %H:%M:%S', '7/22/2011 13:00:00')
doesn't do the trick.
I got this wrong -- shame on me. strftime will only read in ISO 8601 (or timestamp/julian) values. The format specified doesn't apply to the the input -- it is only for the output value.
For this reason the data in the database must still have a natural ordering to be sorted. The suggested formats are ISO 8601 (string), timestamp (integer), and julian-date (float). Using a date like m/d/y in the database won't work for range compares because it fails to adhere to natural ordering.
strftime and friends simply allow generation of one of the values useful for the comparison -- e.g. can convert from ISO 8601/timestamp/julian input to custom/ISO 8601/timestamp/julian-date. They can not directly convert from a custom value though.
The easiest way to fix the database is probably to use a language like Python to write a script for the conversion of existing data. A small Java program could also be written taking advantage of SimpleDateFormat to parse the existing values and write them back using one of the recommended types.
Historical:
Try it in pieces from the console (this will enable must faster experimenting with the query) -- the first conditional then the second. What returns what and why? Once each piece is working then putting them to-gether will work.
There are multiple strftime format mistakes -- see Date and Time for the formats and check the strings again :) Also, if using a saner date format (ISO 8601), could just use datetime and not worry about specifying the format explicitly. ISO 8601 values are is also lexically sortable as strings -- an added advantage.
I am not sure how the Java SQLite adatper will handle a Date when passed as a parameter to a parametrized query (parametrized queries really should be used) ... but in any case this is a secondary issue to the current behavior observed.
Happy coding.
looks like you've got a bad format character
SELECT * FROM data WHERE
strftime('%m/%d/%Y %H:%H:%S', datetime) > strftime('%m/%d/%y %H:%M:%S', '7/22/2011 12:00:00')
-- ^
AND
strftime('%m/%d/%y %H:%M:%S', datetime) < strftime('%m/%d/%y %H:%M:%S', '7/22/2011 13:00:00')

"Catching" Errors from within a user defined function in SQL Server 2005

I have a function that takes a number as an input and converts it to a date. This number isn't any standard form of date number, so I have to manually subdivide portions of the number to various date parts, cast the date parts to varchar strings and then, concatenate and cast the strings to a new datetime object.
My question is how can I catch a casting failure and return a null or low-range value from my function? I would prefer for my function to "passively" fail, returning a default value, instead of returning a fail code to my stored procedure. TRY/CATCH statements apparently don't work form within functions (unless there is some type of definition flag that I am unaware of) and trying the standard '##Error <> 0' method doesn't work either.
Incidentally this sounds like it could be a scalar UDF. This is a performance disaster, as Alex's blog points out. http://sqlblog.com/blogs/alexander_kuznetsov/archive/2008/05/23/reuse-your-code-with-cross-apply.aspx
SELECT CASE WHEN ISDATE(#yourParameter) = 1
THEN CAST(#yourParameter AS DATETIME)
ELSE YourDefaultValue
END
Since the format is nonstandard it sounds to me like you are stuck with doing all the validation yourself, prior to casting. Making sure that the individual pieces are numeric, checking that the month is between 1 and 12, making sure it's not Feb 30, etc. If anything fails you return nothing.

Resources