Time values of minutes & seconds only in SSMS - sql-server

I'm creating a database of music & video files. I would like one of the columns to be the "duration" or "runtime" of the file. Is there a way to show only minutes and seconds in SSMS?
I'm trying to avoid a column that looks like 00:17:30 and rather have it appear as 17:30.

You can store the amount of time of a music/video fragment in several ways. I'll list some, from what I think is the best way to store it to the worst:
As an INT. Store the length in seconds or milliseconds whatever resolution you need. Can go up to 2^31-1 seconds/milliseconds.
As a TIME. Denotes a time, restricted to 23:59:59.9999999 hours. Resolution depends on the width of the TIME column. Problematic if your music/video fragment is longer than 24 hours.
As a VARCHAR. Not really a good storage type, preferred if all you ever want to do with the time is present it. If you want to do queries based on time of music/video you'll have to convert this to another type. Not preferred.
In terms of presentation, a VARCHAR would be easiest as you wouldn't need to format it any further (that's if you stored it the way you want). A TIME value would still need tweaking if you want to format it from a query. An INT would also need preparation to select the value you want to present.
I'd argue that presentation is best kept for the presentation layer. So that would be my advice. If you still insist on selecting the value as it should be presented, I'll give you the way to do it for an INT column with the length in seconds:
DECLARE #total_seconds INT = 2460;
SELECT
CASE WHEN (#total_seconds / (60*60))=0
THEN ''
ELSE (CASE WHEN (#total_seconds / (60*60))<10 THEN '0' ELSE '' END) + CAST(#total_seconds / (60*60) AS VARCHAR)+':'
END +
CASE WHEN ((#total_seconds % (60*60)) / (60))=0
THEN ''
ELSE (CASE WHEN ((#total_seconds % (60*60)) / (60))<10 THEN '0' ELSE '' END) + CAST((#total_seconds % (60*60)) / (60) AS VARCHAR)+':'
END +
(CASE WHEN ((#total_seconds % (60*60)) % (60))<10 THEN '0' ELSE '' END) + CAST((#total_seconds % (60*60)) % (60) AS VARCHAR);

Related

Dimension for geozones or Lat & Long in data warehouse

I have a DimPlace dimension that has the name of the place (manually entered by the user) and the latitude and longitude of the place (automatically captured). Since the Places are entered manually the same place could be in there multiple time with different names, additionally, two distinct places could be very close to each other.
We want to be able to analyze the MPG between two "places" but we want to group them to make a larger area - i.e. using lat & long put all the various spellings of one location, as well as distinct but very close locations, in one record.
I am planning on making a new dimension for this - something like DimPlaceGeozone. I am looking for a resource to help with loading all the lat & long values mapped to ... something?? Maybe postal code, or city name? Sometimes you can find a script to load common dimensions (like DimTime) - I would love something similar for lat & long values in North America?
I've done something similar in the past... The one stumbling block I hit up front was that 2 locations, straddling a border could be physically closer together than 2 locations that are both in the same area.
I got around it by creating a "double grid" system that causes each location to fall into 4 areas. That way 2 locations that share at least 1 "area" you know they are within range of each other.
Here's an example, covering most of the United States...
IF OBJECT_ID('tempdb..#LatLngAreas', 'U') IS NOT NULL
DROP TABLE #LatLngAreas;
GO
WITH
cte_Lat AS (
SELECT
t.n,
BegLatRange = -37.9 + (t.n / 10.0),
EndLatRange = -37.7 + (t.n / 10.0)
FROM
dbo.tfn_Tally(1030, 0) t
),
cte_Lng AS (
SELECT
t.n,
BegLngRange = -159.7 + (t.n / 10.0),
EndLngRange = -159.5 + (t.n / 10.0)
FROM
dbo.tfn_Tally(3050, 0) t
)
SELECT
Area_ID = ROW_NUMBER() OVER (ORDER BY lat.n, lng.n),
lat.BegLatRange,
lat.EndLatRange,
lng.BegLngRange,
lng.EndLngRange
INTO #LatLngAreas
FROM
cte_Lat lat
CROSS JOIN cte_Lng lng;
SELECT
b3.Branch_ID,
b3.Name,
b3.Lat,
b3.Lng,
lla.Area_ID
FROM
dbo.ContactBranch b3 -- replace with DimPlace
JOIN #LatLngAreas lla
ON b3.Lat BETWEEN lla.BegLatRange AND lla.EndLatRange
AND b3.lng BETWEEN lla.BegLngRange AND lla.EndLngRange;
HTH,
Jason

Convert from 4 digit Military time to Standard time

I am using SQL Server 2016 and I'm trying to convert military time to standard time. The military time is a 4 digit integer and I'm trying to get the standard time format (00:00 am/pm).
I ran a simple CASE statement that made it standard but without ':' and 'am/pm'.
CASE
WHEN SA_BEGTIME between 0 and 1259 then SA_BEGTIME
WHEN SA_BEGTIME between 1300 and 2400 then SA_BEGTIME - 1200
ELSE ''
END as Time
Results
How do I convert it so that it is in the right format: '00:00 am/pm'
Thank you!
You can split it into parts with integer division and modulo, cast it to a VARCHAR and then you can convert it to a TIME:
declare #x int = 109
select cast(cast(#x / 100 as varchar(2)) + ':' + cast(#x % 100 as varchar(2)) as time)
Or you can use the new TIMEFROMPARTS() function (SQL Server 2012+):
declare #x int = 109
select TIMEFROMPARTS(#x / 100,#x % 100, 0, 0, 0)
You can then format it however you'd like.
Assuming your data is stored as an integer, and also assuming there is not invalid time stored (i.e. values above 2400 or below 0) you can use the following:
declare #TheTime int = 900
select right(convert(varchar(20),
cast(stuff(right('0000' + convert(varchar(4),#TheTime),4),3,0,':')
as datetime),100),7)
-------
9:00AM
Sorry for the density of the solution. This is what I did:
Convert #TheTime to varchar(4)
Add a string of zeros at the front
Take the rightmost 4 characters from this new string
Stuff a colon sign in the middle of this new string
Cast the string as datetime
Convert back to string using 100 as the style indicator to get AM/PM
Get the right most 7 characters of the string.
I am sure there are more elegant ways, but this one works for me quite well.
I'm using the solution that #BaconBits provided but I had to make a tweak because 2400 was a valid representation but it failed with that code. Here's the tweaked solution:
declare #x int = 2400
select TIMEFROMPARTS((#x / 100) % 24,#x % 100, 0, 0, 0)
I needed to convert a datetime field where the time was military to just the time in AM/PM format. This worked beautifully.
Left(convert(varchar(20), cast(MyDateField as time),100),7)
You can use convert to get n appropriate presentation of time.
declare #mt varchar(4) = '1500'
select convert(varchar, cast(left(#mt, 2) + ':' + right(#mt, 2) as time), 0)

Setting hh:mm to 24 hours format in one single t-sql Query - SQL Server 2012

I got a query which returns some hh:ss time values. The problem however is that it returns it in a PM/AM format while it needs to be a 24 hours format. I can't change the global language setting because this 24 hours time setting is query specific.
I was wondering how to solve this issue?
The query I got now is as follows:
SELECT
dbo.qryMPDisplayPre.Datum, dbo.qryMPDisplayPre.Relatie,
dbo.qryMPDisplayPre.[Order], dbo.qryMPDisplayPre.Status,
dbo.WorkOrder.DeviceID, dbo.Relaties.RelatieNaam AS Monteur,
dbo.Orders.Omschrijving AS OrderOmschrijving,
Format(dbo.WorkOrder.WBTravelDeparture, 'hh:mm') AS TravelDeparture,
Format(dbo.WorkOrder.WBTravelArrival, 'hh:mm') AS TravelArrival,
Format(dbo.WorkOrder.WBWorkArrival, 'hh:mm') AS WorkArrival,
Format(dbo.WorkOrder.WBWorkDeparture, 'hh:mm') AS WorkDeparture,
(CASE WHEN WorkOrder.[WBtravelhours] IS NULL
THEN 0 ELSE (CAST(WorkOrder.[WBTravelHours] * 100.0 / 100.0 AS DECIMAL(30, 2))) END) AS TravelHours,
(CASE WHEN WorkOrder.[wbworkhours] IS NULL
THEN 0 ELSE (CAST(WorkOrder.[WBWorkHours] * 100.0 / 100.0 AS DECIMAL(30, 2))) END) AS WorkHours,
dbo.qryWBMontageGeboekt.Geboekt, dbo.Orders.OpdAdres,
dbo.Orders.OpdPC, dbo.Orders.OpdPlaats,
LEFT(dbo.Orders.Omschrijving, 9) AS Expr1
FROM
dbo.qryWBMontageGeboekt
RIGHT OUTER JOIN
dbo.Orders
RIGHT OUTER JOIN
dbo.Relaties
RIGHT OUTER JOIN
dbo.WorkOrder
RIGHT OUTER JOIN
dbo.qryMPDisplayPre ON dbo.WorkOrder.WONummer = dbo.qryMPDisplayPre.[Order]
AND dbo.WorkOrder.WOStatus = dbo.qryMPDisplayPre.Status
AND dbo.WorkOrder.WOAssignmentDate = dbo.qryMPDisplayPre.Datum
ON dbo.Relaties.RelatieNummer = dbo.qryMPDisplayPre.Relatie
ON dbo.Orders.Nummer = dbo.qryMPDisplayPre.[Order]
ON dbo.qryWBMontageGeboekt.Datum = dbo.qryMPDisplayPre.Datum
AND dbo.qryWBMontageGeboekt.Relatie = dbo.qryMPDisplayPre.Relatie
AND dbo.qryWBMontageGeboekt.[Order] = dbo.qryMPDisplayPre.[Order]
WHERE
(dbo.qryMPDisplayPre.Datum > '11/1/2012')
AND (dbo.qryMPDisplayPre.Status <> 0)
It is kinda weird since the values in WorkArrival are getting displayed correctly in the 24-hours format. Though the values in TravelDeparture, TravelArrival and WorkDeparture aren't while they are formatted the same way as the WorkArrival one.
So this made me believe that there was something wrong with the values from where they are fetched, the WorkOrder table. Though this table contains date times in a 24-hours way and they are all the same (so this couldn't be the problem).
See here the workorder table from where the values are fetched:
As you can see this are all dates with 24 hour HH:MM values.
Now below you can see the Query results with its PM/AM formatted time values:
As you can see the Query results are very weird. It seems that the WorkArrival fields returns its value correct, but the others don't. What is also strange is the fact that the field TravelDeparture returns some off its values correctly (2 top ones) but others incorrect..
Any clue how this can happen, and how I can let the values return in a 24 hours manor (in the query results).
In your example they should all be in 12 hour format, and I see no reason for it not being the case. The format for 12 hours is 'hh' and you are using it in all places.
Is this your original query? If not then check your format strings for upper / lower case. The format for 24 hours happens to be 'HH' (upper case instead of lower case being the only difference).

Weeding out lengthy Durations

I only want to keep durations less than 10 minutes long. my current code is as follows:
Duration = DateDiff(ss, TimeBegin, TimeEnd)
TimeBegin and TimeEnd are in TIME format. Obviously Duration right now comes back as:
00:00:07
That's where I'm running into trouble. Can I use a statement that looks like this:
<= 00:10:00 or <= '00:10:00'
Essentially I want:
Duration = (Datediff(ss, TimeBegin, TimeEnd) *ONLY IF LESS THAN 10 MINUTESg)
I already state earlier in the query that if no result is returned to create a NULL, so when a duration is Greater than 10 minutes, I just want it to be ignored as if it didn't exist.
DateDiff(ss, TimeBegin, TimeEnd) gives you the difference in seconds. Just use a Case statement to return the value only if that's under 600 (...ELSE Null is implied):
set #Duration = CASE
WHEN DateDiff(ss, #TimeBegin, #TimeEnd) < 600
THEN DateDiff(ss, #TimeBegin, #TimeEnd)
END;
Agreed with #aucuparia. But (this is for topic starter) be careful with using datediffs is seconds instead of minutes. Their behavior isn't the same, like months/years datediffs. I mean server rounds your operands:
select datediff(mi,'2014-05-15 19:00:00','2014-05-15 19:10:59')
is not the same like
select datediff(ss,'2014-05-15 19:00:00','2014-05-15 19:10:59')
Just execute them both and see the difference. The first one still is 10-minutes difference, but the second will be cut off by 600-seconds 'where' clause.

Check last bit in a hex in SQL

I have a SQL entry that is of type hex (varbinary) and I want to do a SELECT COUNT for all the entries that have this hex value ending in 1.
I was thinking about using CONVERT to make my hex into a char and then use WHERE my_string LIKE "%1". The thing is that varchar is capped at 8000 chars, and my hex is longer than that.
What options do I have?
Varbinary actually works with some string manipulation functions, most notably substring. So you can use eg.:
select substring(yourBinary, 1, 1);
To get the first byte of your binary column. To get the last bit then, you can use this:
select substring(yourBinary, len(yourBinary), 1) & 1;
This will give you zero if the bit is off, or one if it is on.
However, if you really only have to check at most the last 4-8 bytes, you can easily use the bitwise operators on the column directly:
select yourBinary & 1;
As a final note, this is going to be rather slow. So if you plan on doing this often, on large amounts of data, it might be better to simply create another bit column just for that, which you can index. If you're talking about at most a thousand rows or so, or if you don't care about speed, fire away :)
Check last four bits = 0001
SELECT SUM(CASE WHEN MyColumn % 16 IN (-15,1) THEN 1 END) FROM MyTable
Check last bit = 1
SELECT SUM(CASE WHEN MyColumn % 2 IN (-1,1) THEN 1 END) FROM MyTable
If you are wondering why you have to check for negative moduli, try SELECT 0x80000001 % 16
Try using this where
WHERE LEFT(my_string,1) = 1
It it's text values ending in 1 then you want the Right as opposed to the Left
WHERE RIGHT(my_string,1) = 1

Resources