How to display a time in specific format - sql

Using SQL Server 2005
Table1
ID TimeColumn
001 13.00
002 03.30
003 14.00
004 23.00
005 08.30
...
Table1 Format
TimeColumn Format: HH:MM
TimeColumn Datatype is nvarchar
TimeColumn will display the time One Hour or HalfHour
TimeColumn will not display 08.20, 08.56. It will display the time like 08.00, 08.30.
I want to display a time like 13 instead of 13.00, 3.5 instead of 03.30.
Expected Output
ID TimeColumn Value
001 13.00 13
002 03.30 3.5
003 14.00 14
004 23.00 23
005 18.30 18.5
...
How to make a query for the above condition?

Based on your facts, there are only 2 cases for the last 3 digits, either
.30; or
.00
So we just replace them
SELECT
ID,
TimeColumn,
Value = replace(replace(TimeColumn, '.30', '.5'), '.00', '')
From Table1
EDIT
To drop the leading 0, you can use this instead (the Value column is numeric)
SELECT
ID,
TimeColumn,
Value = round(convert(float,TimeColumn)*2,0)/2
From Table1
Or if you need it to be varchar
SELECT
ID,
TimeColumn,
Value = right(round(convert(float,TimeColumn)*2,0)/2,5)
From Table1

Try this
SELECT
DATEPART(hour,TimeColumn) +
1 / DATEPART(minute,TimeColumn) * 60
AS Value
FROM Table1
This is where TimeColumn is DateTime. For Column Type NVarChar use a String function to split hours and minutes.

I believe that is how SQL Server stores datetime, you then format it with your flavor of programming language.

Here is how you can do it:
select SUBSTRING('20.30', 1, 2) + (case when SUBSTRING('20.30', 4, 2) = '30' then '.5' else '' end)
just replace '20.30' with your column name and add from clause

Declare #table table (ID nvarchar(10),timevalue nvarchar(10))
INSERT INTO #table values('001','13.00')
INSERT INTO #table values('002','03.30')
INSERT INTO #table values('003','14.00')
INSERT INTO #table values('004','23.00')
INSERT INTO #table values('005','08.30')
select (CASE WHEN (CHARINDEX('.3',timevalue)>0) then convert(varchar(2),timevalue,2)
else convert(varchar(2),timevalue,2) + '.5'
end)
from #table

With TestInputs As
(
Select '001' As Id, Cast('13.00' As nvarchar(10)) As TimeColumn
Union All Select '002','03.30'
Union All Select '003','14.00'
Union All Select '004','23.00'
Union All Select '005','08.30'
Union All Select '006','08.26'
Union All Select '007','08.46'
Union All Select '008','08.56'
)
, HoursMinutes As
(
Select Id, TimeColumn
, Cast( Substring(TimeColumn
, 1
, CharIndex('.', TimeColumn) - 1 ) As int ) As [Hours]
, Cast( Substring(TimeColumn
, CharIndex('.', TimeColumn) + 1
, Len(TimeColumn) ) As int ) As [Minutes]
From TestInputs
)
Select Id, TimeColumn
, [Hours] + Case
When [Minutes] < 30 Then 0.0
When [Minutes] < 60 Then 0.5
Else 1
End As Value
From HoursMinutes

Related

SQL get consecutive days ignoring weekend

I have a table with following format:
ID ID1 ID2 DATE
1 1 1 2018-03-01
2 1 1 2018-03-02
3 1 1 2018-03-05
4 1 1 2018-03-06
5 1 1 2018-03-07
6 2 2 2018-03-05
7 2 2 2018-03-05
8 2 2 2018-03-06
9 2 2 2018-03-07
10 2 2 2018-03-08
From this table I have to get all records where ID1 and ID2 are the same in that column and where DATE is 5 consecutive work days (5 dates in a row, ignoring missing dates for Saturday/Sunday; ignore holidays).
I have really no idea how to achieve this. I did search around, but couldn't find anything that helped me. So my question is, how can I achieve following output?
ID ID1 ID2 DATE
1 1 1 2018-03-01
2 1 1 2018-03-02
3 1 1 2018-03-05
4 1 1 2018-03-06
5 1 1 2018-03-07
SQLFiddle to mess around
Assuming you have no duplicates and work is only on weekdays, then there is a simplish solution for this particular case. We can identify the date 4 rows ahead. For a complete week, it is either 4 days ahead or 6 days ahead:
select t.*
from (select t.*, lead(dat, 4) over (order by id2, dat) as dat_4
from t
) t
where datediff(day, dat, dat_4) in (4, 6);
This happens to work because you are looking for a complete week.
Here is the SQL Fiddle.
select t.* from
(select id1,id2,count(distinct dat) count from t
group by id1,id2
having count(distinct dat)=5) t1 right join
t
on t.id1=t1.id1 and t.id2=t1.id2
where count=5
Check this-
Dates of Two weeks with 10 valid dates
http://sqlfiddle.com/#!18/76556/1
Dates of Two weeks with 10 non-unique dates
http://sqlfiddle.com/#!18/b4299/1
and
Dates of Two weeks with less than 10 but unique
http://sqlfiddle.com/#!18/f16cb/1
This query is very verbose without LEAD or LAG and it is the best I could do on my lunch break. You can probably improve on it given the time.
DECLARE #T TABLE
(
ID INT,
ID1 INT,
ID2 INT,
TheDate DATETIME
)
INSERT #T SELECT 1,1,1,'03/01/2018'
INSERT #T SELECT 2,1,1,'03/02/2018'
INSERT #T SELECT 3,1,1,'03/05/2018'
INSERT #T SELECT 4,1,1,'03/06/2018'
INSERT #T SELECT 5,1,1,'03/07/2018'
--INSERT #T SELECT 5,1,1,'03/09/2018'
INSERT #T SELECT 6,2,2,'03/02/2018'
INSERT #T SELECT 7,2,2,'03/05/2018'
INSERT #T SELECT 8,2,2,'03/05/2018'
--INSERT #T SELECT 9,2,2,'03/06/2018'
INSERT #T SELECT 10,2,2,'03/07/2018'
INSERT #T SELECT 11,2,2,'03/08/2018'
INSERT #T SELECT 12,2,2,'03/15/2018'
INSERT #T SELECT 13,1,1,'04/01/2018'
INSERT #T SELECT 14,1,1,'04/02/2018'
INSERT #T SELECT 15,1,1,'04/05/2018'
--SELECT * FROM #T
DECLARE #LowDate DATETIME = DATEADD(DAY,-1,(SELECT MIN(TheDate) FROM #T))
DECLARE #HighDate DATETIME = DATEADD(DAY,1,(SELECT MAX(TheDate) FROM #T))
DECLARE #DaysThreshold INT = 5
;
WITH Dates AS
(
SELECT DateValue=#LowDate
UNION ALL
SELECT DateValue + 1 FROM Dates
WHERE DateValue + 1 < #HighDate
),
Joined AS
(
SELECT * FROM Dates LEFT OUTER JOIN #T T ON T.TheDate=Dates.DateValue
),
Calculations AS
(
SELECT
ID=MAX(J1.ID),
J1.ID1,J1.ID2,
J1.TheDate,
LastDate=MAX(J2.TheDate),
LastDateWasWeekend = CASE WHEN ((DATEPART(DW,DATEADD(DAY,-1,J1.TheDate) ) + ##DATEFIRST) % 7) NOT IN (0, 1) THEN 0 ELSE 1 END,
Offset = DATEDIFF(DAY,MAX(J2.TheDate),J1.TheDate)
FROM
Joined J1
LEFT OUTER JOIN Joined J2 ON J2.ID1=J1.ID1 AND J2.ID2=J1.ID2 AND J2.TheDate<J1.TheDate
WHERE
NOT J1.ID IS NULL
GROUP BY J1.ID1,J1.ID2,J1.TheDate
)
,FindValid AS
(
SELECT
ID,ID1,ID2,TheDate,
IsValid=CASE
WHEN LastDate=TheDate THEN 0
WHEN LastDate IS NULL THEN 1
WHEN Offset=1 THEN 1
WHEN Offset>3 THEN 0
WHEN Offset<=3 THEN
LastDateWasWeekend
END
FROM
Calculations
UNION
SELECT DISTINCT ID=NULL,ID1,ID2, TheDate=#HighDate,IsValid=0 FROM #T
),
FindMax As
(
SELECT
This.ID,This.ID1,This.ID2,This.TheDate,MaxRange=MIN(Next.TheDate)
FROM
FindValid This
LEFT OUTER JOIN FindValid Next ON Next.ID2=This.ID2 AND Next.ID1=This.ID1 AND This.TheDate<Next.TheDate AND Next.IsValid=0
GROUP BY
This.ID,This.ID1,This.ID2,This.TheDate
),
FindMin AS
(
SELECT
This.ID,This.ID1,This.ID2,This.TheDate,This.MaxRange,MinRange=MIN(Next.TheDate)
FROM
FindMax This
LEFT OUTER JOIN FindMax Next ON Next.ID2=This.ID2 AND Next.ID1=This.ID1 AND This.TheDate<Next.MaxRange-- AND Next.IsValid=0 OR Next.TheDate IS NULL
GROUP BY
This.ID,This.ID1,This.ID2,This.TheDate,This.MaxRange
)
,Final AS
(
SELECT
ID1,ID2,MinRange,MaxRange,SequentialCount=COUNT(*)
FROM
FindMin
GROUP BY
ID1,ID2,MinRange,MaxRange
)
SELECT
T.ID,
T.ID1,
T.ID2,
T.TheDate
FROM #T T
INNER JOIN Final ON T.TheDate>= Final.MinRange AND T.TheDate < Final.MaxRange AND T.ID1=Final.ID1 AND T.ID2=Final.ID2
WHERE
SequentialCount>=#DaysThreshold
OPTION (MAXRECURSION 0)

Select rows by applying whatever condition(if or case)

I have a set of records suppose below:
Id Name status date
1 xx 1 2016-06-27 14:05:17.447
2 yy 2 2016-06-27 14:05:17.447
3 zz 1 2016-06-27 14:05:17.447
4 aa 2 2016-06-27 14:05:17.447
5 bb 2 2016-06-27 14:05:17.447
I want to select all the rows from above but
for the rows who have status=1, i want to apply a condition that
select if status=1 and date<=getdate
How can I do that?
select
Id,
Name,
case when status =1 and date<getdate() then 'I want to select this row' else I don't want to select this row end as statuss
,date
from
yourtable
Update: as per your edit,you need to apply your conditions in where clause
select * from yourtable
where status=1 and date<getdate()
The simplest way I can think of, unless status is nullable:
SELECT Id, Name, Status, Date
FROM TableName
WHERE status <> 1
OR date <= getdate()
If it is nullable, you can do this:
SELECT Id, Name, Status, Date
FROM TableName
WHERE ISNULL(status, 0) <> 1
OR date <= getdate()
Try this:
CREATE TABLE #Status
(
Id INT
, Name CHAR(2)
, status INT
, date DATETIME
);
INSERT INTO #Status
( Id, Name, status, date )
VALUES ( 1 -- Id - int
, 'xx' -- Name - char(2)
, 1 -- status - int
, '2016-06-27 14:05:17.447' -- date - datetime
),
( 2 -- Id - int
, 'yy' -- Name - char(2)
, 2 -- status - int
, '2016-06-27 14:05:17.447' -- date - datetime
),
( 3 -- Id - int
, 'zz' -- Name - char(2)
, 1 -- status - int
, '2016-06-27 14:05:17.447' -- date - datetime
),
( 4 -- Id - int
, 'aa' -- Name - char(2)
, 2 -- status - int
, '2016-06-27 14:05:17.447' -- date - datetime
),
( 5 -- Id - int
, 'bb' -- Name - char(2)
, 2 -- status - int
, '2016-06-27 14:05:17.447' -- date - datetime
),
( 6 -- Id - int
, 'cc' -- Name - char(2)
, 1 -- status - int
, '2016-07-27 14:05:17.447' -- date - datetime
);
SELECT *
FROM #Status;
WITH cte
AS ( SELECT *
FROM #Status
WHERE status <> 1
) ,
cteStatus1
AS ( SELECT *
FROM #Status
WHERE status = 1
AND date <= GETDATE()
)
SELECT *
FROM cte
UNION
SELECT *
FROM cteStatus1;

How to order the numbers?

Table1
id value
---------
1 100
2A 200
2 300
10 500
8 200
....
Select *
from table1
order by id
Showing output as
id value
------------
1 100
10 500
2A 200
2 300
8 200
....
How to make a proper order?
Expected output
id value
----------
1 100
2 300
2A 200
8 200
10 500
....
If it is fixed that last character may be character then you can try following query
WITH A(ID)
AS
(
SELECT '1'
UNION ALL
SELECT '2C'
UNION ALL
SELECT '2A'
UNION ALL
SELECT '2'
UNION ALL
SELECT '10'
)
SELECT *
FROM A
ORDER BY
convert(int,
Case When IsNumeric(ID) = 0 then left(ID,len(id)-1)
Else ID END
) , Case When IsNumeric(ID) = 0 then RIGHT(ID,1) Else '0' END
and if it is variable then you can write a function that replace charecter with its ansi value or 0 . and then put order by close on that column .
CREATE FUNCTION [dbo].[RemoveNonAlphaCharacters](#Temp varchar(1000))
RETURNS int
AS
BEGIN
WHILE PatIndex ('%[^0-9]%', #Temp) > 0
SET #Temp = Stuff( #Temp, PatIndex('%[^0-9]%' , #Temp ), 1, '')
RETURN #Temp
END
SELECT id, value
FROM dbo.Table1
ORDER BY [dbo].[RemoveNonAlphaCharacters](id) ASC
SELECT
LEFT(ID,1),
RIGHT(ID,1),
*
FROM table1
ORDER BY LEFT(ID,1),RIGHT(ID,1)
should do the trick, I'm not even sure if the left and right is needed in the selected statement.
Select *
from table1
order by cast(replace(lower(id), 'abcdefg', '') as int),
replace(id, '0123456789','');
SELECT * FROM table1 ORDER BY CAST(id as varchar(50))

SQL Query - Help Needed for retrieval of values in a text field

I have a table that looks like follows:
NAME(varchar(6), STRING(varchar(250)
ABCD '1 2 1 173 1 8 9 1 1 2 4 7 1 3.....'
APLC '1 3 11 34 1 4 99 33 23 111 12 6 7 8....'
the string continues with this up to 250 characters.
What I am trying to do is get the values and there respective positions from this string.
I know I can use Charindex but that gives me only the first position of a number in the string.
e.g.
Select Charindex ('2',STRING) where Name = ABCD
ANSWER = 7
But what I'm looking for is something like a table that have the following in for each Name
Name Position Value
---------------------------
ABCD, 7, 2
ABCD, 1, 1
ABCD, 13, 1
ABCD, 18, 1
ABCD, 19, 7
Any ideas welcome :)
With a little help of a numbers table it could look like this.
select T.Name,
N.N as Position,
substring(T.STRING, N.N, 1) as Value
from YourTable as T
cross apply Numbers as N
where N.N between 1 and 250 and
substring(T.STRING, N.N, 1) <> ' '
Working sample with table variable and master..spt_values as a numbers table.
declare #T table
(
NAME varchar(6),
STRING varchar(250)
)
insert into #T values
('ABCD', '1 2 1 173 1 8 9 1 1 2 4 7 1 3'),
('APLC', '1 3 11 34 1 4 99 33 23 111 12 6 7 8')
;with Numbers(N) as
(
select Number
from master..spt_values
where type = 'P'
)
select T.Name,
N.N as Position,
substring(T.STRING, N.N, 1) as Value
from #T as T
cross apply Numbers as N
where N.N between 1 and 250 and
substring(T.STRING, N.N, 1) <> ' '
This approach would work for multi-digit numbers. If 173 should result in three result rows, check Mikael Eriksson's or podiluska's answer.
; with cte as
(
select 1 as start
, case
when patindex('%[0-9] %', string) > 0 then patindex('%[0-9] %', string)
else len(string)
end as [length]
, name
, string
from YourTable
union all
select start + [length] as start
, case
when patindex('%[0-9] %',
substring(string, start + [length], len(string)-start + [length]))
> 0 then patindex('%[0-9] %',
substring(string, start + [length], len(string)-start + [length]))
else len(string)-start + [length]
end as [length]
, name
, string
from cte
where start + [length] < len(string)
)
select Name
, start + patindex('%[0-9]%', substring(string, [start], [length])) - 1 as Position
, ltrim(substring(string, [start], [length])) as Value
from cte
Live example at SQL Fiddle.
Where #t is your table...
;WITH numbers ( n ) AS
(
select 1 as n
union all
select 1 + n FROM numbers WHERE n < 250
)
select name, n as position, SUBSTRING(string,n,1) as value
from #t, numbers
where SUBSTRING(string,n,1)<>' '
order by Name, n
option (maxrecursion 250)
If, on the other hand, you want to treat consecutive numbers as being a one number...
;WITH Pieces(name, pn, start, [stop], string) AS
(
SELECT name, 1, 1, CHARINDEX(' ', string),string from #t
UNION ALL
SELECT pieces.name, pn + 1, stop + 1, CHARINDEX(' ', pieces.string, stop + 1), pieces.string
FROM Pieces
inner join #t on pieces.name = #t.name
WHERE stop > 0
)
select *
from
( SELECT name, start as position,
SUBSTRING(string, start, CASE WHEN stop > 0 THEN stop-start ELSE 300 END) AS value
FROM Pieces
) v
where RTRIM(value)>''

How to limit the selection in SQL Server by sum of a column?

Can I limit rows by sum of a column in a SQL Server database?
For example:
Type | Time (in minutes)
-------------------------
A | 50
B | 10
C | 30
D | 20
E | 70
...
And I want to limit the selection by sum of time. For example maximum of 100 minutes. Table must look like this:
Type | Time (in minutes)
-------------------------
A | 50
B | 10
C | 30
Any ideas? Thanks.
DECLARE #T TABLE
(
[Type] CHAR(1) PRIMARY KEY,
[Time] INT
)
INSERT INTO #T
SELECT 'A',50 UNION ALL
SELECT 'B',10 UNION ALL
SELECT 'C',30 UNION ALL
SELECT 'D',20 UNION ALL
SELECT 'E',70;
WITH RecursiveCTE
AS (
SELECT TOP 1 [Type], [Time], CAST([Time] AS BIGINT) AS Total
FROM #T
ORDER BY [Type]
UNION ALL
SELECT R.[Type], R.[Time], R.Total
FROM (
SELECT T.*,
T.[Time] + Total AS Total,
rn = ROW_NUMBER() OVER (ORDER BY T.[Type])
FROM #T T
JOIN RecursiveCTE R
ON R.[Type] < T.[Type]
) R
WHERE R.rn = 1 AND Total <= 100
)
SELECT [Type], [Time], Total
FROM RecursiveCTE
OPTION (MAXRECURSION 0);
Or if your table is small
SELECT t1.[Type],
t1.[Time],
SUM(t2.[Time])
FROM #T t1
JOIN #T t2
ON t2.[Type] <= t1.[Type]
GROUP BY t1.[Type],t1.[Time]
HAVING SUM(t2.[Time]) <=100