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.
Related
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]));
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
I have a query in which I want to select data from a column where the data is a date. The problem is that the data is a mix of text and dates.
This bit of SQL only returns the longest text field:
SELECT MAX(field_value)
Where the date does occur, it is always in the format xx/xx/xxxx
I'm trying to select the most recent date.
I'm using MS SQL.
Can anyone help?
Try this using ISDATE and CONVERT:
SELECT MAX(CONVERT(DateTime, MaybeDate))
FROM (
SELECT MaybeDate
FROM MyTable
WHERE ISDATE(MaybeDate) = 1) T
You could also use MAX(CAST(MaybeDate AS DateTime)). I got in the (maybe bad?) habit of using CONVERT years ago and have stuck with it.
To do this without a conversion error:
select max(case when isdate(col) = 1 then cast(col as date) end) -- or use convert()
from . . .
The SQL statement does not specify the order of operations. So, even including a where clause in a subquery will not guarantee that only dates get converted. In fact, the SQL Server optimizer is "smart" enough to do the conversion when the data is brought in and then do the filtering afterwards.
The only operation that guarantees sequencing of operations is the case statement, and there are even exceptions to that.
Another solution would be using PATINDEX in WHERE clause.
SELECT PATINDEX('[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]', field_value)
Problem with this approach is you really are not sure if something is date (e.g. 99/99/9999 is not date).
And problem with IS_DATE is it depends on configuration (e.g. DATEFORMAT).
So, use an appropriate option.
I am trying to update my SQL DB when the the date has expired (i.e is less than now)
I have this lovely bit of SQL code
Update Notifications
SET Active = 'N'
where CAST(SetDate AS DATE) <= CAST('2012-08-23 11:19:00 +0000' AS DATE)
But it updates all the records (even if the date is not less than now)
I have also tried
Update Notifications
SET Active = 'N'
where CAST(SetDate AS DATE) < CAST('2012-08-23 11:19:00 +0000' AS DATE)
But this dosent affect any rows.
I guess I have something a little confused??
Any help???
Thanks
I cannot see anything odd about the code, except for this line:
CAST('2012-08-23 11:19:00 +0000' AS DATE)
If you are using CAST() to change it to the date, then there is no need to pass in the time portion of the value.
You did not provide the full table schema but one thing to consider is using a bit field for the Y/N values.
Here is a SQL Fiddle with the code working
Update Notifications
SET Active = 'N'
where CAST(SetDate AS DATE) <= CAST('2012-08-23' AS DATE)
One thing you have confused is types in SQL. I don't know what database you are using, but it probably has types for boolean (or int) and date. Storing booleans and dates as strings is very inefficient. For example, even if your example did work it would have to scan the entire database and cast each value in order to compare it.
The clue to the error is the < vs <=. This hints at the values being equal. If the cast fails on both sides, and returns the default value of zero, then then will be equal.
You should change your schema to use the correct types, then your query will work.
Is there a way to write a query equivalent to
select * from log_table where dt >= 'nov-27-2009' and dt < 'nov-28-2009';
but where you could specify only 1 date and say you want the results for that entire day until the next one.
I'm just making this up, but something of the form:
select * from log_table where dt = 'nov-27-2009':+1;
I do not believe there is one method that is portable to all RDBMSes.
A check in one of my references (SQL Cookbook) shows that no one RDBMS solves the problem quite the same way. I would recommend checking out Chapter 8 of that book, which covers all of the different methods for DB2, Oracle, PostgreSQL, MySQL.
I've had to deal with this issue in SQLite, though, and SQL Cookbook doesn't address that RDBMS, so I'll mention a bit about it here. SQLite doesn't have a date/time data type; you have to create your own by storing all date/time data as TEXT and ensure that your application enforces its formatting. SQLite does have a set of date/time conversion functions that allow you to add nominal date/times while maintaining the data as strings. If you need to add two time durations (HH:MM:SS) to each other, though, based upon data that you've stored in text columns that you are treating as date/time data, you'll have to write your own functions (search for "Defining SQLite User Functions") and attach them to the database at runtime via a call to sqlite3_create_function(). If you want an example of some user functions that add time values, let me know.
For MS SQL Server, check out DATEPART.
/* dy = Day of Year */
select * from log_table where datepart(dy, dt) = datepart(dy, '2009-nov-27');
With SQL Server, you could
Select * From table
Where dt >= DateAdd(day, DateDiff(day, 0, #ParamDate), 0)
And dt < DateAdd(day, DateDiff(day, 0, #ParamDate), 1)
As long as you are dealing with the date data type for the respective data type, the following will work:
t.date_column + 1
...will add one day to the given date. But I have yet to find a db that allows for implicit data type conversion into a date.
SELECT '12-10-2009' + 1
...will fail on SQL Server because SQL Server only performs the implicit conversion when comparing to a datetime data type column. So you need to use:
SELECT CONVERT(DATETIME, '12-10-2009') + 1
For Oracle, you'd have to use the TO_DATE function; MySQL would use something like STR_TO_DATE, etc.
Have a column that just has the date part (time is 00:00:00.000) and then you can add a where clause: WHERE dt = '2009-11-27'