Sorting SQL Query by Numbers as String - sql

I am working on creating some reports from data that someone else created the databases for and am having trouble getting proper sorts.
This report is supposed to show the 20 most recent entries, but the data does not include a timestamp, instead it was created using two separate varchar fields, one for the date in the format MM/dd/yyyy and one for the time in the format HH:mm:ss AM/PM. This is causing issues when I try to select the most recent data.
When I use:
SELECT top 20 * FROM SignIn order by Checkin desc, Intime desc
I get the correct date, but the times are the values that start with 9:59am and go down from there alphabetically with 6am immediately after 7pm.
After doing much research I tried, among other things:
SELECT * FROM SignIn order by Checkout desc, CONVERT(varchar(30), outtime, 101) desc
The results were the same.
I don't do much in MS SQL, I am more fluent with with MySql and have never seen any reason to have dates stored as straight strings before. I am at a loss as to getting it to sort correctly.
I have tried creating the report with the html, php, and javascript handling the sort and display instead of having SQL just access the first records, but the load time is ridiculous since it loads the full 5000 pieces of data and then sorts them and clips all but the first 20.
As much as I wish I could, I can't change the database storage at this time, so I need something that will work with the varchars and handle the am\pm distinction if at all possible.
Any help would be appreciated.

You can concatenate the two columns and cast the result as datetime and use it for sorting.
SELECT top 20 *
FROM SignIn
ORDER BY cast(Checkout+' '+outtime as datetime) desc
Note that this assumes all the dates and times are valid. In case you may have incorrect values, use try_cast (for SQL Server versions 2012 and later)
SELECT top 20 *
FROM SignIn
WHERE try_cast(Checkout+' '+outtime as datetime) is not null --invalid values get filtered out
ORDER BY cast(Checkout+' '+outtime as datetime) desc

Just you have to concatenate your date time columns and convert to date like the following:
SELECT * FROM SignIn order by CONVERT(DATETIME, [dateColumn] + ' ' + [TimeColumn], 101) desc
you can learn more about datetime format in this tutorials link

Related

Access query with date BETWEEN breaks when using parameters

I have an SQL query in access that will grab all records where a calculated date is in between two values. It works fine if I hardcode date literals such as:
SELECT *
FROM Table
WHERE DateAdd("d",-60,DateAdd("yyyy",65,[Table].[BirthDate])) Between #3/21/2021# And #3/27/2021#;
However I need to parametrize the the between dates so that they can be entered by a user like:
SELECT *
FROM Table
WHERE DateAdd("d",-60,DateAdd("yyyy",65,[Table].[BirthDate])) Between [StartDate] And [EndDate];
However when I run the latter query and enter the exact same dates as the former, hard-coded one, it starts pulling records outside the between range. I've attempted to enter the dates like 3/21/2021 as well as date literals like #3/21/2021# and neither work. The latter doesn't pull anything at all.
I also have a form with a handful of text boxes using the short date format that let the user pick the dates for the query. It has the same issue of pulling back incorrect records. None of the records have any time component to my knowledge.
How can I get the date between to correctly work with user entered parameters?
Access doesn't know what data type your parameters are, so specify that in the query:
PARAMETERS
StartDate DateTime,
EndDate DateTime;
SELECT
*
FROM
Table
WHERE
DateAdd("d",-60,DateAdd("yyyy",65,[Table].[BirthDate])) Between [StartDate] And [EndDate];
Parameters worked to filter a native field - not the calculated date. However, using CDate() function worked.
SELECT *
FROM Table
WHERE DateAdd("d",-60,DateAdd("yyyy",65,[Table].[BirthDate]))
Between CDate([StartDate]) And CDate([EndDate]);
But before I remembered that, I tested calculating with inputs back to a birthdate range which also worked.
SELECT *
FROM Table
WHERE [BirthDate] Between DateAdd("d",60,DateAdd("yyyy",-65,[StartDate]))
And DateAdd("d",60,DateAdd("yyyy",-65,[EndDate]));

Date/Time as string in Access SQL - Select a specific date

I would like run a SQL in MS Access like the following:
SELECT Time, Ask, Bid
FROM AUDCAD
WHERE Time LIKE '2016.10.05'
ORDER BY ID;
However the result is nothing, The Time field data is look like the following:
2016.12.05 09:42:17.026
2016.12.05 09:42:17.387
2016.12.05 09:42:17.951
2016.12.05 09:42:18.464
...
2016.12.06 09:24:41.449
2016.12.06 09:24:41.854
2016.12.06 09:24:42.258
Therefore, I would like to extract the data day by day (this example: 2016.10.05)
Can anyone help me to solve this problem?
Lawrence
You need to check two things first...
Did your insert query work without errors .. are you sure time '2016.10.5' data exist in your DB?
Can you execute standard query to get time data and it works? Meaning can you 'SELECT FROM AUDCAD" ang get time data 2016.10.5
You must use the proper syntax for date expressions in Access SQL:
SELECT [Time], Ask, Bid
FROM AUDCAD
WHERE [Time] = #2016/10/05#
ORDER BY ID;
or, if Time has a time component:
SELECT [Time], Ask, Bid
FROM AUDCAD
WHERE Fix([Time]) = #2016/10/05#
ORDER BY ID;
However, it looks like you retrieve data from a DateTime2 field in SQL Server.
Thus, either change the data type to DateTime, or use the SQL Native Driver version 10 or 11 for your ODBC connection. If not, you will receive the date/time as text, not date/time values.
The separator for DateTime fields is #. Ex: #12/30/2016#.
I would recommend to always use the american order in VBA (m/d/y) even if the local machine is configured otherwise. It works fine that way.
Your sample query
SELECT Time, Ask, Bid
FROM AUDCAD
WHERE Time LIKE '2016.10.05'
ORDER BY ID;
uses a LIKE clause without any wildcards, so WHERE Time LIKE '2016.10.05' behaves just the same as WHERE Time = '2016.10.05'. That won't return any rows because the [Time] column always includes some characters after the date.
By default, Access uses the asterisk (*) as the "0 or more characters" wildcard, so
SELECT Time, Ask, Bid
FROM AUDCAD
WHERE Time LIKE '2016.10.05 *'
ORDER BY ID;
should work. Alternatively, you could use the ALIKE ("ANSI LIKE") keyword with the percent (%) wildcard:
SELECT Time, Ask, Bid
FROM AUDCAD
WHERE Time ALIKE '2016.10.05 %'
ORDER BY ID;

How to translate this SQL query to tableau?

I have a SQL query which shows time activity of each account. Database is Microsoft SQL Server on Windows Server 2008.
Help me please to translate this query to tableau with using parameters Parameters.Date1 and Parameters.Date2 instead of #time.
The result of the query:
USER,Date,Total time
USER1,2016-09-22,07:00:00.0000000
USER2,2016-09-22,08:00:00.0000000
USER3,2016-09-22,05:00:00.0000000
SQL query:
DECLARE #time datetime
set #time = '08.09.2016'
SELECT
[User],
CAST(DATEADD(SECOND, sum(datediff(DAY, #time, [Start])), #time) AS date) 'Date',
CAST(DATEADD(SECOND, sum(datediff(SECOND, '00:00:00',[Period])), '00:00:00') AS time) 'Total time'
FROM
[User].[dbo].[UserAction]
WHERE
[Start] >= #time+'00:00:00' and [Start] <= #time+'23:59:59'
GROUP BY
[USER]
input data to build the query:
USER, Start,End,Period
USER1,2016-09-22 09:00:00.000,2016-09-22 12:00:00.000,03:00:00
USER1,2016-09-22 12:00:00.000,2016-09-22 13:00:00.000,01:00:00
USER1,2016-09-22,13:00:00.000,2016-09-22 16:00:00.000,03:00:00
USER2,2016-09-22,09:00:00.000,2016-09-22 13:00:00.000,04:00:00
USER2,2016-09-22,13:00:00.000,2016-09-22 17:00:00.000,04:00:00
USER3,2016-09-22,09:00:00.000,2016-09-22 10:00:00.000,01:00:00
USER3,2016-09-22,10:00:00.000,2016-09-22 12:00:00.000,02:00:00
USER3,2016-09-22,12:00:00.000,2016-09-22 14:00:00.000,02:00:00
I don't have enough imaginary stack overflow points yet to make a comment instead of an answer, but I would agree with Gordon Linoff.
A table valued function in sql can be used directly in a Tableau data source, and it's treated just like a table.
Note I did not test the below, but here is what the equivalent function might look like:
CREATE FUNCTION dbo.MyFuntion (#time datetime)
RETURNS TABLE
AS
RETURN
(
SELECT
[User]
,cast(DATEADD(SECOND, sum(datediff(DAY, #time,[Start])),#time) as date)'Date'
,cast(DATEADD(SECOND, sum(datediff(SECOND, '00:00:00',[Period])),'00:00:00') as time)'Total time'
FROM
[User].[dbo].[UserAction]
WHERE
[Start] >= #time+'00:00:00' and [Start] <= #time+'23:59:59'
GROUP BY [USER]
);
Tableau 9 (haven't tried 10) seems to discourage custom SQL (it warns anyone that opens your workbook) and stored procedures (slow vs. same sql in a function).
Alternatively, adding the pure dbo.UserAction table to a data source and making calculated fields for the second two columns might work: Tableau Documentation. It seems to have all the functions needed to manipulate dates. However, there may be some crazy limitation associated with parameters that might limit it, honestly can't remember off the top of my head.
You don't need custom SQL for this. Keep it simple. Connect Tableau directly to your UserAction table.
You can either:
Put Day(Start) on the filter shelf, Make sure it is a continuous Date truncated to the Day. Show the filter and set the filter to let you pick a single value at a time - I would choose a slider UI.
Or write a calculated field to put on the filters shelf that references a parameter such as day(Start) = day(Date1)
Put User on one shelf, such as rows, and Sum(Period) on another such as columns. That should do it unless Tableau has trouble interpreting your Period field datatype. If so, try changing the datatype to Number inside Tableau to see if it converts durations to numbers automatically, if not you may need to write a calculated field for the conversion.

Why this Query is returning different results for different date formats?

I have a table named book_data with batch_dt as column name of type varchar in sql server.
when I pass the query
SELECT DISTINCT batch FROM book_data
it gives me the following results
batch_dt
-------------
2012-10-31
-------------
2012-11-01
-------------
2012-11-02
-------------
2012-11-03
-------------
.
.
.
Now what I am doing is getting the total count of records between two dates. Fairly a simple query.
SELECT COUNT(*) FROM book_data WHERE CONVERT(varchar(12),CONVERT(datetime,batch_dt),101) BETWEEN '11/02/2012' and '10/31/2012'
the result is
112
and just by changing the month from 02 to 2 the query gives me 218 results
SELECT COUNT(*) FROM book_data WHERE CONVERT(varchar(12),CONVERT(datetime,batch_dt),101) BETWEEN '11/2/2012' and '10/31/2012'
why this different behaviour?
Use CAST(batch_dt AS DATE) instead, and pass the date in a language neutral manner, in the format YYYYMMDD. This way it will be comared as a date not as a varchar:
SELECT COUNT(*)
FROM book_data
WHERE CAST(batch_dt AS DATE)
BETWEEN '20121102' and '20121130'
But, this is not safe, if there was any value in barch_dt in a wrong format, you will get a casting error. In this case you can add ISDATE(batch_dt) = 1 to ensure that it is a valid data time. But it is better to make this column of datatype DateTime.
Another thing to note: is that BETWEEN is asymmetric in SQL Server, meaning that BETWEEN '11/02/2012' and '10/31/2012' is evaluated as:
DATE >= '11/02/2012'
AND
DATE <= '10/31/2012'
which will never be true, the reason it works for you is that the dates were be compared as strings not as a dates. But you have to keep it like BETWEEN the small value and the biggest value.
You compare string with BETWEEN. If you do so you need to make sure that you compare in the correct order => YYYYMMDD MM:SS would be a correct order.
If you can, add columns with type datetime and store real date time values in your database. If you can not do that you can split up the values and build a date value yourself. This is much slower then just use a CONVERT() or CAST() but you can make sure that it works even with wrong date-strings.
You can use PATINDEX(),LEFT(),RIGHT() keywords to get the values you need or you use a split() function (you can find many version google it, e.g. https://codereview.stackexchange.com/questions/15125/sql-server-split-function-optimized). If you use the split function, then split by / and then get year, month, day from the positions.
But best would be still to have correct datetime values stored in your db.
You get this different behavior because you don't compare the dates but the strings/varchars.
For a Date (or DateTime), 10/2/2012 is the same as 10/02/2012.
But for string, these values are (of course) different. It's just as if you'd compare 'abcd' with 'ab0cd'
SELECT COUNT(*)
FROM book_data
WHERE CONVERT(DATETIME,batch_dt,101) BETWEEN '11/2/2012' and '10/31/2012'
This would be more appropriate

Order by descending date - month, day and year

This seems stupid but, I simply need a list of dates to be ordered with the most recent date at top. Using order by DESC doesn't seem to be working the way I want it to.
SELECT *
FROM vw_view
ORDER BY EventDate DESC
It gives me the date ordered by month and day, but doesn't take year into consideration.
for example:
12/31/2009
12/31/2008
12/30/2009
12/29/2009
Needs to be more like:
12/31/2009
12/30/2009
12/29/2009
12/28/2009
and so on.
I'm guessing EventDate is a char or varchar and not a date otherwise your order by clause would be fine.
You can use CONVERT to change the values to a date and sort by that
SELECT *
FROM
vw_view
ORDER BY
CONVERT(DateTime, EventDate,101) DESC
The problem with that is, as Sparky points out in the comments, if EventDate has a value that can't be converted to a date the query won't execute.
This means you should either exclude the bad rows or let the bad rows go to the bottom of the results
To exclude the bad rows just add WHERE IsDate(EventDate) = 1
To let let the bad dates go to the bottom you need to use CASE
e.g.
ORDER BY
CASE
WHEN IsDate(EventDate) = 1 THEN CONVERT(DateTime, EventDate,101)
ELSE null
END DESC
try ORDER BY MONTH(Date),DAY(DATE)
Try this:
ORDER BY YEAR(Date) DESC, MONTH(Date) DESC, DAY(DATE) DESC
Worked perfectly on a JET DB.
You have the field in a string, so you'll need to convert it to datetime
order by CONVERT(datetime, EventDate ) desc
Assuming that you have the power to make schema changes the only acceptable answer to this question IMO is to change the base data type to something more appropriate (e.g. date if SQL Server 2008).
Storing dates as mm/dd/yyyy strings is space inefficient, difficult to validate correctly and makes sorting and date calculations needlessly painful.
what is the type of the field EventDate, since the ordering isn't correct i assume you don't have it set to some Date/Time representing type, but a string. And then the american way of writing dates is nasty to sort
If you restructured your date format into YYYY/MM/DD then you can use this simple string ordering to achieve the formating you need.
Alternatively, using the SUBSTR(store_name,start,length) command you should be able to restructure the sorting term into the above format
perhaps using the following
SELECT *
FROM vw_view
ORDER BY SUBSTR(EventDate,6,4) + SUBSTR(EventDate, 0, 5) DESC
Try this
SELECT *
FROM vw_view
ORDER BY DATE_FORMAT(EventDate, "%m-%d-%y") DESC