Insert blank row to result after ORDER BY - sql

I have SQL Query, I want to add insert blank row in result so it is easy to see the result.
I want to insert it after ORDER BY. don't know if it could be done.
Here is my select statement.
SELECT TableName.CREWACTIONFACTID
,TableName.CREWKEY as CrewKey
,TableName.EVENTKEY as EventID
,TableName.ACTIONSEQUENCE
,case TableName.ACTIONTYPE
when 'DISPATCHED' then '2-Dispatched'
when 'ASSIGNED' then '1-Assigned'
when 'ENROUTE' then '3-Entoute'
when 'ARRIVED' then '4-Arrived'
else 'unknown'
end as Type
,TableName.STARTDATETIME as StartTime
,TableName.ENDDATETIME as EndTIme
,TableName.DURATION as Duration
FROM DatabaseName.TableName TableName
where
To_Date(to_char(TableName.STARTDATETIME, 'DD-MON-YYYY')) >= To_Date('?DATE1::?','MM/DD/YYYY')
AND To_Date(to_char(TableName.ENDDATETIME, 'DD-MON-YYYY')) <= To_Date('?DATE2::?','MM/DD/YYYY')
ORDER BY TableName.EVENTKEY, TableName.STARTDATETIME,TableName.ACTIONSEQUENCE

You can, pretty much as Michael and Gordon did, just tack an empty row on with union all, but you need to have it before the order by:
...
and to_date(to_char(t.enddatetime, 'DD-MON-YYYY')) <=
to_date('?DATE2::?','MM/DD/YYYY')
union all
select null, null, null, null, null, null, null, null
from dual
order by eventid, starttime, actionsequence;
... and you can't use the case that Gordon had directly in the order by because it isn't a selected value - you'll get an ORA-07185. (Note that the column names in the order by are the aliases that you assigned in the select, not those in the table; and you don't include the table name/alias; and it isn't necessary to alias the null columns in the union part, but you may want to for clarity).
But this relies on null being sorted after any real values, which may not always be the case (not sure, but might be affected by NLS parameters), and it isn't known if the real eventkey can ever be null anyway. So it's probably safer to introduce a dummy column in both parts of the query and use that for the ordering, but exclude it from the results by nesting the query:
select crewactionfactid, crewkey, eventid, actionsequence, type,
starttime, endtime, duration
from (
select 0 as dummy_order_field,
t.crewactionfactid,
t.crewkey,
t.eventkey as eventid,
t.actionsequence,
case t.actiontype
when 'DISPATCHED' then '2-Dispatched'
when 'ASSIGNED' then '1-Assigned'
when 'ENROUTE' then '3-Enroute'
when 'ARRIVED' then '4-Arrived'
else 'unknown'
end as type,
t.startdatetime as starttime,
t.enddatetime as endtime,
t.duration
from schema_name.table_name t
where to_date(to_char(t.startdatetime, 'DD-MON-YYYY')) >=
to_date('?DATE1::?','MM/DD/YYYY')
and to_date(to_char(t.enddatetime, 'DD-MON-YYYY')) <=
to_date('?DATE2::?','MM/DD/YYYY')
union all
select 1, null, null, null, null, null, null, null, null
from dual
)
order by dummy_order_field, eventid, starttime, action sequence;
The date handling is odd though, particularly the to_date(to_char(...)) parts. It looks like you're just trying to lose the time portion, in which case you can use trunk instead:
where trunc(t.startdatetime) >= to_date('?DATE1::?','MM/DD/YYYY')
and trunc(t.enddatetime) <= to_date('?DATE2::?','MM/DD/YYYY')
But applying any function to the date column prevents any index on it being used, so it's better to leave that alone and get the variable part in the right state for comparison:
where t.startdatetime >= to_date('?DATE1::?','MM/DD/YYYY')
and t.enddatetime < to_date('?DATE2::?','MM/DD/YYYY') + 1
The + 1 adds a day, so id DATE2 was 07/12/2012, the filter is < 2012-07-13 00:00:00, which is the same as <= 2012-07-12 23:59:59.

Your question is rather complicated. SQL only guarantees the ordering of results, through the order by. It does not guarantee what happens afterwards. So, you have to put in the blank row and then add the information afterwords:
<your select query minus the order by>
union all
select NULL as CrewActionFatId, . . .
order by (case when CrewActionFactId is NULL then 1 else 0 end),
TableName.EVENTKEY, TableName.STARTDATETIME,TableName.ACTIONSEQUENCE
In practice, #Michael's solution would normally work. But it is not guaranteed.
Also, you should decide whether you want blanks or NULLs. I'm guessing the first id is a number, so I'm setting it to NULL.
In general, such presentation niceties are handled by the calling application. Perhaps you need a better SQL query tool to see the data more cleanly.
Here is what the full query would look like (with all fields set to NULL, you can change to blank if you prefer):
SELECT TableName.CREWACTIONFACTID, TableName.CREWKEY as CrewKey,
TableName.EVENTKEY as EventID, TableName.ACTIONSEQUENCE,
(case TableName.ACTIONTYPE
when 'DISPATCHED' then '2-Dispatched'
when 'ASSIGNED' then '1-Assigned'
when 'ENROUTE' then '3-Entoute'
when 'ARRIVED' then '4-Arrived'
else 'unknown'
end) as Type,
TableName.STARTDATETIME as StartTime,
TableName.ENDDATETIME as EndTIme,
TableName.DURATION as Duration
FROM DatabaseName.TableName TableName
where To_Date(to_char(TableName.STARTDATETIME, 'DD-MON-YYYY')) >= To_Date('?DATE1::?','MM/DD/YYYY') AND
To_Date(to_char(TableName.ENDDATETIME, 'DD-MON-YYYY')) <= To_Date('?DATE2::?','MM/DD/YYYY')
union all
SELECT NULL AS CREWACTIONFACTID, NULL AS CrewKey, NULL AS EventID,
NULL AS ACTIONSEQUENCE, NULL AS Type, NULL AS StartTime, NULL AS EndTime,
NULL AS Duration
from dual
ORDER BY (case when CrewActionFactId is NULL then 1 else 0 end),
TableName.EVENTKEY, TableName.STARTDATETIME, TableName.ACTIONSEQUENCE

An odd request to be sure, but yes it can be done by making a UNION against a row of literal blank values. To make sure the order-by is applied to the real query, enclose the whole thing in () and then union it against the blank row.
SELECT * FROM
(SELECT TableName.CREWACTIONFACTID
,TableName.CREWKEY as CrewKey
,TableName.EVENTKEY as EventID
,TableName.ACTIONSEQUENCE
,case TableName.ACTIONTYPE
when 'DISPATCHED' then '2-Dispatched'
when 'ASSIGNED' then '1-Assigned'
when 'ENROUTE' then '3-Entoute'
when 'ARRIVED' then '4-Arrived'
else 'unknown'
end as Type
,TableName.STARTDATETIME as StartTime
,TableName.ENDDATETIME as EndTIme
,TableName.DURATION as Duration
FROM DatabaseName.TableName TableName
where
To_Date(to_char(TableName.STARTDATETIME, 'DD-MON-YYYY')) >= To_Date('?DATE1::?','MM/DD/YYYY')
AND To_Date(to_char(TableName.ENDDATETIME, 'DD-MON-YYYY')) <= To_Date('?DATE2::?','MM/DD/YYYY')
ORDER BY TableName.EVENTKEY, TableName.STARTDATETIME,TableName.ACTIONSEQUENCE
)
UNION ALL
SELECT
'' AS CREWACTIONFACTID,
'' AS CrewKey,
'' AS EventID,
'' AS ACTIONSEQUENCE,
'' AS Type,
'' AS StartTime,
'' AS EndTime,
'' AS Duration
FROM dual
Finally, depending on how you are presenting this result, I would look into other methods of spacing out the result. Appending blank rows to a query for presentation purposes flies in the face of separation of business and presentation logic.

The result will be desplayed in an HTML page.
So, use SQL to extract the data, not to format the output.
Depending on the page structure and layout, there are a lot of solutions.
Have a look here.

Related

group by issue in sql

i'm trying to get in a new column the sessions who are between 08:00 and 18:00. You can see my last CASE in the CTE. For each date there should be a new column "TotalRestrictedSessions" which indicate how many session were on that particular date. If there are none, in this case i have to write 0. I suspect that my problem is when i convert the DATE?
WITH ParkeonCTE
AS
(
SELECT
OccDate = CONVERT(DATE, OC.LocalStartTime),
TotalOccSessions = COUNT(OC.SessionId),
AuthorityId,
TotalOccDuration = ISNULL(SUM(OC.DurationMinutes),0),
TotalNumberOfOverstay = SUM(CAST(OC.IsOverstay AS INT)),
TotalMinOfOverstays = ISNULL(SUM(OC.OverStayDurationMinutes),0),
(CASE
WHEN OC.OspId IS NULL THEN 'OffStreet' ELSE 'OnStreet'
END
) AS ParkingContextType,
(CASE
WHEN CAST(OC.LocalStartTime AS TIME) >= '08:00:00' AND CAST(OC.LocalStartTime AS TIME) <=
'18:00:00'
THEN COUNT(OC.SessionId)
END
) AS TotalRestrictedSessions
FROM Analytics.OccupancySessions AS OC
WHERE OC.AuthorityId IS NOT NULL
GROUP BY CONVERT(DATE,OC.LocalStartTime), OC.AuthorityId,OC.OspId
)
SELECT OC.OccDate,
OC.ParkingContextType,
OC.AuthorityId,
OC.TotalRestrictedSessions,
SUM(OC.TotalOccSessions) AS TotalOccSessions,
AVG(OC.TotalOccDuration) AS AvgOccMinutesDuration, -- wrong
SUM(OC.TotalOccDuration) AS TotalOccDuration,
SUM(OC.TotalNumberOfOverstay) AS TotalNumberOfOverstay,
SUM(OC.TotalMinOfOverstays) AS TotalMinOfOverstays,
CAST(AVG(OC.TotalMinOfOverstays) AS decimal(10,2)) AS AvgMinOfOverstays -- wrong
FROM ParkeonCTE AS OC
GROUP BY OC.OccDate, OC.AuthorityId, OC.ParkingContextType
ORDER BY OC.OccDate DESC
You just need to move your aggregation outside of your CASE expression, called conditional aggregation.
SUM(CASE
WHEN CAST(OC.LocalStartTime AS TIME) >= '08:00:00'
AND CAST(OC.LocalStartTime AS TIME) <= '18:00:00'
THEN 1
ELSE 0
END
) AS TotalRestrictedSessions
Generally, you should include the current query results and your desired results in your question to make it easier to figure out where the issues are.

Sub query inside a decode statement in oracle

I have data in a table like below:
EMPLOYEE_NUM START_DTM END_DTM
47567567446 5/9/2019 12:00:00 PM
76475676575675756 5/10/2019 12:00:00 PM 5/10/2019 11:59:59 PM
456756765767 5/3/2019 12:00:00 PM 5/8/2019 11:59:59 PM
74676576764565 5/2/2019 12:00:00 PM 5/8/2019 11:59:59 PM
98695689576 5/1/2019 12:00:00 PM
I want retrieve employee_num with below conditions:
if there are any entries with NULL as end_dtm,
then print employee_num with max(start_dtm) and whose end_dtm is null
if there are no entries with NULL as end_dtm,
then print employee_num with end_dtm=max(end_dtm)
I tried with
select
decode(select count(1) from employee where end_dtm is null,
0,
select employee_num where end_dtm=(select max(end_dtm) from employee),
select employee_num where start_dtm=(select max(start_dtm) from employee where end_dtm is null),
) from dual
Looks like this is not a valid query.
Could anybody please help.
So, you must distinguish between END_DTM being null vs. non-null first, and only then consider either END_DTM or START_DTM for further ordering.
The function NVL2 is exactly what you need for the first part. It takes three arguments; it returns the second argument when the first is non-null, and the third argument when it's null. So: NVL2(END_DTM, 1, 0) will return 1 when the date is non-null and 0 when it is null. And then, you can use NVL2 again, to choose END_DTM when it is not null, but START_DTM when it is null.
You can then use this in defining ROW_NUMBER(), in the ORDER BY clause, like so:
select [whatever]
from
( select t.*,
row_number() over (order by nvl2(END_DTM, 1, 0),
nvl2(END_DTM, END_DTM, START_DTM) desc) rn
from your_table t
)
where rn = 1
;
If END_DTM is always non-null (or if it is always null) then the first NVL2 value is constant, so the ordering is only by END_DTM (if END_DTM is always non-null), resp. by START_DTM (if END_DTM is always null). But if there is a mix of null and non-null END_DTM, then only those with null END_DTM are tied by the first ordering criterion, and then only those rows are considered for selecting the max START_DTM.
I would have used analytical function for this as following:
Select employee_num from
(Select t.employee_num
Sum(case when end_dtm is null then 1 end) as cnt,
Row_number()
over (order by end_dtm desc nulls last) as end_dtm_rn,
Row_number()
over (order by case when end_dtm is null then start_dtm end desc nulls last) as start_dtm_rn
From employee t)
Where case when cnt = 0
then end_dtm_rn
Else start_dtm_rn end = 1;
Cheers!!
Just use aggregation and case:
select employee_num,
(case when count(*) = count(end_dtm) -- no nulls
then max(end_dtm)
else max(start_dtm)
end)
from t
group by employee_num;
If you want this per row in the original data, use analytic functions:
select t.*,
(case when count(*) over (partition by employee_num) = count(end_dtm) over (partition by employee_num) -- no nulls
then max(end_dtm) over (partition by employee_num)
else max(start_dtm) over (partition by employee_num)
end)
from t;
EDIT:
An alternative reading of what you want suggests that you want one employee_num subquery to your conditions. If that is the case, then this is even simpler:
select t.*
from (select t.*
from t
order by (case when end_dtm is null then 1 else 2 end), -- nulls first
start_dtm desc
) t
where rownum = 1;

MS SQL SERVER LAG

I'm trying to apply a condition to LAG in a SQL query. Does anyone know how to do this?
This is the query:
SELECT CONCAT([FirstName],' ',[LastName]) AS employee,
CAST([ArrivalTime] AS DATE) AS date,
CAST(DATEADD(hour,2,FORMAT([ArrivalTime],'HH:mm')) AS TIME) as time,
CASE [EventType]
WHEN 20001 THEN 'ENTRY'
ELSE 'EXIT'
END AS Action,
OutTime =
CASE [EventType]
WHEN '20001'
THEN DATEDIFF(minute,Lag([ArrivalTime],1) OVER(ORDER BY [CardHolderID], [ArrivalTime]), [ArrivalTime])
ELSE
NULL
END
FROM [CCFTEvent].[dbo].[ReportEvent]
LEFT JOIN [CCFTCentral].[dbo].[Cardholder] ON [CCFTEvent].[dbo].[ReportEvent].[CardholderID] = [CCFTCentral].[dbo].[Cardholder].[FTItemID]
WHERE EventClass = 41
AND [FirstName] IS NOT NULL
AND [FirstName] LIKE 'Leeann%'
The problem I have is when the times are subtracted between two different dates, it must also be NULL when subtracting between two different dates.
The 910 is incorrect.
I'd add another condition to your case statement. i.e.
...
CASE
WHEN [EventType] = '20001' AND DATEDIFF(DAY,[ArrivalTime],LAG([ArrivalTime]) over (ORDER BY [CardHolderID], [ArrivalTime])) > 0
THEN NULL
WHEN [EventType] = '20001'
THEN DATEDIFF(minute,Lag([ArrivalTime],1) OVER(ORDER BY [CardHolderID], [ArrivalTime]), [ArrivalTime])
ELSE NULL
It seems to me that the LAG just needs to be partitioned by the date (& some other fields for good measure).
If the previous date is in another partition,
then the LAG will return NULL,
then the datediff will return NULL.
SELECT
CONCAT(holder.FirstName+' ', holder.LastName) AS employee,
CAST(repev.ArrivalTime AS DATE) AS [date],
CAST(SWITCHOFFSET(repev.ArrivalTime,'+02:00') AS TIME) as [time],
IIF(repev.EventType = 20001, 'ENTRY', 'EXIT') AS Action,
(CASE WHEN repev.EventType = 20001
THEN DATEDIFF(minute, LAG(repev.ArrivalTime)
OVER (PARTITION BY repev.EventClass, repev.CardholderID, CAST(repev.ArrivalTime AS DATE)
ORDER BY repev.ArrivalTime), repev.ArrivalTime)
END) AS OutTime
FROM [CCFTEvent].[dbo].[ReportEvent] AS repev
LEFT JOIN [CCFTCentral].[dbo].[Cardholder] AS holder ON holder.FTItemID = repev.CardholderID
WHERE repev.EventClass = 41
AND holder.FirstName LIKE 'Leeann%'
Test on db<>fiddle here

COUNT from DISTINCT values in multiple columns

If this has been asked before, I apologize, I wasn't able to find a question/solution like it before breaking down and posting. I have the below query (using Oracle SQL) that works fine in a sense, but not fully what I'm looking for.
SELECT
order_date,
p_category,
CASE
WHEN ( issue_grp = 1 ) THEN '1'
ELSE '2/3 '
END AS issue_group,
srt AS srt_level,
COUNT(*) AS total_orders
FROM
database.t_con
WHERE
order_date IN (
'&Enter_Date_YYYYMM'
)
GROUP BY
p_category,
CASE
WHEN ( issue_grp = 1 ) THEN '1'
ELSE '2/3 '
END,
srt,
order_date
ORDER BY
p_category,
issue_group,
srt_level,
order_date
Current Return (12 rows):
Needed Return (8 rows without the tan rows being shown):
Here is the logic of total_order column that I'm expecting:
count of order_date where (srt_level = 80 + 100 + Late) ... 'Late' counts needed to be added to the total, just not be displayed
I'm eventually adding a filled_orders column that will go before the total_orders column, but I'm just not there yet.
Sorry I wasn't as descriptive earlier. Thanks again!
You don't appear to need a subquery; if you want the count for each combination of values then group by those, and aggregate at that level; something like:
SELECT
t1.order_date,
t1.p_category,
CASE
WHEN ( t1.issue_grp = 1 ) THEN '1'
ELSE '2/3 '
END AS issue_group,
t1.srt AS srt_level,
COUNT(*) AS total_orders
FROM
database.t_con t1
WHERE
t1.order_date = TO_DATE ( '&Enter_Date_YYYYMM', 'YYYYMM' )
GROUP BY
t1.p_category,
CASE
WHEN ( t1.issue_grp = 1 ) THEN '1'
ELSE '2/3 '
END,
t1.srt,
t1.order_date
ORDER BY
p_category,
issue_group,
srt_level,
order_date;
You shouldn't be relying on implicit conversion and NLS settings for your date argument (assuming order_date is actually a date column, not a string), so I've used an explicit TO_DATE() call, using the format suggested by your substitution variable name and prompt.
However, that will give you the first day of the supplied month, since a day number isn't being supplied. It's more likely that you either want to prompt for a full date, or (possibly) just the year/month but want to include all days in that month - which IN() will not do, if that was your intention. It also implies that stored dates all have their time portions set to midnight, as that is all it will match on. If those values have non-midnight times then you need a range to pick those up too.
I got it working to the extent of what my question was. Just needed to nest each column where counts/calculations were happening.
SELECT
order_date,
p_category,
issue_group,
srt_level,
order_count,
SUM(order_count) OVER(
PARTITION BY order_date, issue_group, p_category
) AS total_orders
FROM
(
SELECT
order_date,
p_category,
CASE
WHEN ( issue_grp = 1 ) THEN '1'
ELSE '2/3 '
END AS issue_group,
srt AS srt_level,
COUNT(*) AS order_count
FROM
database.t_con
WHERE
order_date IN (
'&Enter_Date_YYYYMM'
)
GROUP BY
p_category,
CASE
WHEN ( issue_grp = 1 ) THEN '1'
ELSE '2/3 '
END,
srt,
order_date
)
ORDER BY
order_date,
p_category,
issue_group

TSQL - Multiple aliases for CASE WHEN statement

I have a query that selects columns from a table where the condition is met, the problem is this logic is repeated several times (once for each column required) and wondered if there was a better way of doing it.
SELECT
CASE
WHEN (date1 IS NULL) THEN [date2]
WHEN (date1 IS NOT NULL OR ( date1 IS NOT NULL AND date2 IS NOT NULL )) THEN [date1]
ELSE ''
END AS selectedDate
//Repeat above statement for [day], [hour], [minute]
I want to know if its possible to aggregate a number of these case statements under the same statement with separate aliases as they all rely on the same conditions instead of rewriting the same statement multiple times for each column, for example I tried the following:
SELECT
CASE
WHEN (date1 IS NULL) THEN [date2] as "selectedDate", [day2] as "day", [hour2] as "hour", [minute2] as "minute"
WHEN (date1 IS NOT NULL OR ( date1 IS NOT NULL AND date2 IS NOT NULL )) THEN [date1] as "selectedDate", [day1] as "day", [hour1] as "hour", [minute1] as "minute"
ELSE ''
Not in the way you are describing...
An alternative would be to use isnull() or coalesce():
select
SelectedDate = coalesce(date1,date2)
, [Day] = coalesce(day1,day2)
, [Hour] = coalesce(hour1,hour2)
, [Minute] = coalesce(minute1,minute2)
from t
No. Each column needs its own select statement.
For your particular application of this CASE statement, you could be using COALESCE instead.
SELECT COALESCE(date1, date2) AS selectedDate FROM SomeTable