Group by function column - plsql

I'm writing a query like this...
select to_char(e_date, 'MON/YYYY') as Month , location_code, count(employee_number) from...
Now I want to group by Month and Location_code. so how to use to_char(e_date, 'MON/YYYY') as Month in group by clause?
EDIT:
select to_char(vheda.e_date, 'MON/YYYY') as Months , hla.location_code, count(vheda.employee_number) emp_count from
virtu.virt_hr_emp_daily_attendance vheda
inner join per_all_people_f papf on vheda.party_id = papf.party_id
inner join per_all_assignments_f paaf on papf.person_id = paaf.person_id
inner join hr_locations_all hla on paaf.location_id = hla.location_id
where (trunc(sysdate) between PAPF.EFFECTIVE_START_DATE and PAPF.EFFECTIVE_END_DATE)
--and (vheda.e_in_time is not null)
and vheda.e_duration <> 0
and (trunc(sysdate) between PAAF.EFFECTIVE_START_DATE and PAAF.EFFECTIVE_END_DATE)
and vheda.e_date between '1-aug-2014' and '31-oct-2014'
group by hla.location_code, vheda.e_date
order by vheda.e_date
OUT PUT WHEN USE GROUP BY CLAUSE group by to_char(vheda.e_date, 'MON/YYYY'), hla.location_code:
ORA-00979: not a GROUP BY expression
00979. 00000 - "not a GROUP BY expression"
*Cause:
*Action:
Error at Line: 58 Column: 37

select to_char(e_date, 'MON/YYYY') as Month , location_code, count(employee_number)
from ...
group by to_char(e_date, 'MON/YYYY'), location_code

You need to group by To_char(vheda.e_date, 'MON/YYYY') and hla.location_code.
SELECT To_char(vheda.e_date, 'MON/YYYY') AS Months,
hla.location_code,
Count(vheda.employee_number) emp_count
FROM virtu.virt_hr_emp_daily_attendance vheda
inner join per_all_people_f papf
ON vheda.party_id = papf.party_id
inner join per_all_assignments_f paaf
ON papf.person_id = paaf.person_id
inner join hr_locations_all hla
ON paaf.location_id = hla.location_id
WHERE ( Trunc(SYSDATE) BETWEEN PAPF.effective_start_date AND
PAPF.effective_end_date )
--and (vheda.e_in_time is not null)
AND vheda.e_duration <> 0
AND ( Trunc(SYSDATE) BETWEEN PAAF.effective_start_date AND
PAAF.effective_end_date )
AND vheda.e_date BETWEEN '1-aug-2014' AND '31-oct-2014'
GROUP BY To_char(vheda.e_date, 'MON/YYYY'),
hla.location_code
ORDER BY vheda.e_date
For example, let's see the same with EMP table,
SQL> SELECT To_char(hiredate, 'MON/YYYY') AS Months,
2 deptno,
3 Count(empno) emp_count
4 FROM emp
5 GROUP BY To_char(hiredate, 'MON/YYYY'),
6 deptno
7 /
MONTHS DEPTNO EMP_COUNT
-------- ---------- ----------
DEC/1980 20 1
JUN/1981 10 1
NOV/1981 10 1
MAY/1987 20 1
FEB/1981 30 2
MAY/1981 30 1
DEC/1981 30 1
JAN/1982 10 1
SEP/1981 30 2
DEC/1981 20 1
APR/1981 20 1
APR/1987 20 1
12 rows selected.
SQL>

select EXTRACT(MONTH FROM e_date)||'/'|| EXTRACT(Year FROM e_date) as months , location_code, count(employee_number)
FROM ...
group by EXTRACT(MONTH FROM e_date)||'/'|| EXTRACT(Year FROM e_date), location_code
You can also use EXTRACT function in GROUP BY clause..
i hope it helps..

Related

Display hourly based data for 24 hour in SQL Server

I want to display the hourly based report for the last 24 hour. I have tried but the problem is that it will display count only where particular hour contains data.
But I want to display count for an hour and if count not found then display 0 over there.
select
datepart(hour, upload_date) as [hour], count(*)
from
tbl_stories
where
upload_date > getdate() - 1
group by
datepart(hour, upload_date)
Output:
hour count
-------------
11 2
16 1
17 1
but I want to get a record in the following way.
hour count
-------------
1 0
2 0
3 5
.
.
.
.
24 1
You can use a value() clause to generate all the hours and then use left join:
select v.hh, count(s.upload_date)
from (values (0), (1), . . . (23)
) v(hh) left join
tbl_stories s
on datepart(hour, s.upload_date) = v.hh and
s.upload_date > getdate() - 1
group by v.hh
order by v.hh;
Note that hours go from 0 to 23.
If you don't want to list out the hours, a convenient generation method is a recursive CTE:
with hours as (
select 1 as hh
union all
select hh + 1
from hours
where hh < 23
)
select h.hh, count(s.upload_date)
from hours h
tbl_stories s
on datepart(hour, s.upload_date) = h.hh and
s.upload_date > getdate() - 1
group by h.hh
order by h.hh;

Create multiple rows based off a date range

I have a calendar query and a table below. I have a StartDate and end date for a member. Also on my calendar table I have captured a "Weekof" based on the startDate. I would like to capture if a member is active anytime during that weekof. See expected results.
SELECT DISTINCT
--CA.CALENDAR_DATE,
TO_CHAR(CALENDAR_DATE,'MM/DD/YYYY') AS CALENDAR_DATE
TO_CHAR(NEXT_DAY(CALENDAR_DATE, 'Monday') - 7, 'MM/DD/YY-') ||
TO_CHAR(NEXT_DAY(CALENDAR_DATE, 'Monday') - 1, 'MM/DD/YY') AS WEEK_OF_YEAR,
ROW_NUMBER () OVER ( ORDER BY CALENDAR_DATE) AS MasterCalendar_RNK
FROM CALENDAR CA
WHERE 1=1
--AND CA.CALENDAR_DATE BETWEEN ADD_MONTHS(TRUNC(SYSDATE), -12) AND TRUNC(SYSDATE)
--AND CA.CALENDAR_DATE BETWEEN TRUNC(SYSDATE) -5 AND TRUNC(SYSDATE)
ORDER BY TO_DATE(CALENDAR_DATE,'MM/DD/YYYY') DESC
Table
Member StartDate EndDate
A 1/31/17
B 2/1/17 2/15/17
Expected output:
Member StartDate EndDate Week_Of_Year Active
A 1/31/17 1/30/17-2/5/17 1
A 1/31/17 2/6/17-2/12/17 1
A 1/31/17 2/13/17-2/19/17 1
B 2/1/17 2/15/17 1/30/17/2/5/17 1
B 2/1/17 2/15/17 2/6/17-2/12/17 1
B 2/1/17 2/15/17 2/13/17-2/19/17 1
Current Query:
WITH MASTER_CALENDAR AS (
SELECT TRUNC(SYSDATE) + 1 - LEVEL , A.CALENDAR_DATE
FROM (SELECT C.CALENDAR_DATE FROM MST.CALENDAR C WHERE 1=1 AND C.CALENDAR_DATE > SYSDATE-30 AND C.CALENDAR_DATE < SYSDATE) A
WHERE 1=1
CONNECT BY LEVEL <= 1 --NEED TO UPDATE?
ORDER BY A.CALENDAR_DATE DESC
),
ActiveMembers AS (
SELECT H.CLT_CLT_PGMID, H.START_DT
,CASE WHEN TRUNC(H.END_DT) = '1-JAN-3000'
THEN SYSDATE
ELSE TO_DATE(H.END_DT)
END AS END_DT
FROM H
WHERE 1=1
AND H.CLT_CLT_PGMID IN ('1','2','3')
)
SELECT CLT_CLT_PGMID, STARTDATE, ENDDATE, WEEK_OF_YEAR, ACTIVE -- but not week_start
FROM (
SELECT DISTINCT A.CLT_CLT_PGMID,
TO_CHAR(A.START_DT, 'MM/DD/YY') AS STARTDATE,
TO_CHAR(A.END_DT, 'MM/DD/YY') AS ENDDATE,
NEXT_DAY(CAL.CALENDAR_DATE, 'Monday') - 7 AS WEEK_START, -- for ordering later
TO_CHAR(NEXT_DAY(CAL.CALENDAR_DATE, 'Monday') - 7, 'MM/DD/YY-') ||
TO_CHAR(NEXT_DAY(CAL.CALENDAR_DATE, 'Monday') - 1, 'MM/DD/YY') AS WEEK_OF_YEAR,
1 AS ACTIVE
FROM ActiveMembers A
INNER JOIN MASTER_CALENDAR CAL ON CAL.CALENDAR_DATE BETWEEN A.START_DT AND A.END_DT
--BETWEEN TO_CHAR(A.START_DT,'MM/DD/YYYY') AND COALESCE(A.END_DT,(SYSDATE))
)
WHERE 1=1
ORDER BY
CLT_CLT_PGMID , STARTDATE, ENDDATE, WEEK_START
;
Since the calendar query currently generates strings, it would be simpler to go back to the calendar table, join that to your member/date table, and regenerate the week range string:
With CTEs to represent your calendar table (just with dates for the last few weeks for now) and member data:
with calendar(calendar_date) as (
select trunc(sysdate) + 1 - level from dual connect by level <= 42
),
mytable (member, startdate, enddate) as (
select cast('A' as varchar2(6)), date '2017-01-31', cast (null as date) from dual
union all select cast('B' as varchar2(6)), date '2017-02-01', date '2017-02-15' from dual
)
select member, startdate, enddate, week_of_year, active -- but not week_start
from (
select distinct m.member,
to_char(m.startdate, 'MM/DD/YY') as startdate,
to_char(m.enddate, 'MM/DD/YY') as enddate,
next_day(c.calendar_date, 'Monday') - 7 as week_start, -- for ordering later
to_char(next_day(c.calendar_date, 'Monday') - 7, 'MM/DD/YY-') ||
to_char(next_day(c.calendar_date, 'Monday') - 1, 'MM/DD/YY') as week_of_year,
1 as active
from mytable m
join calendar c
on c.calendar_date between m.startdate and coalesce(m.enddate, trunc(sysdate))
)
order by member, startdate, enddate, week_start;
gets
MEMBER STARTDAT ENDDATE WEEK_OF_YEAR ACTIVE
------ -------- -------- ----------------- ----------
A 01/31/17 01/30/17-02/05/17 1
A 01/31/17 02/06/17-02/12/17 1
A 01/31/17 02/13/17-02/19/17 1
A 01/31/17 02/20/17-02/26/17 1
B 02/01/17 02/15/17 01/30/17-02/05/17 1
B 02/01/17 02/15/17 02/06/17-02/12/17 1
B 02/01/17 02/15/17 02/13/17-02/19/17 1
You haven't specified an upper limit for members with no end-date, so I've used today, via coalesce().
The inner query is only needed for ordering, as the week range string can't be used, and you don't want to see the week start on its own; and you can't use distinct and order by a field you aren't selecting.
I'd do this in a similar way to Alex, but slightly different. Seeing as your weeks start with a Monday, I'd use TRUNC(dt, 'iw') to get the ISO start of the week (which happens to be defined as a Monday) for the specified date. Then I'd get the distinct values of those before joining to your table, like so:
with calendar as (select trunc(sysdate) - level + 1 calendar_date
from dual
connect by level <= 50),
your_table as (select 'A' member, date '2017-01-31' startdate, NULL enddate from dual union all
select 'B' member, date '2017-02-01' startdate, date '2017-02-15' enddate from dual)
select yt.member,
yt.startdate,
yt.enddate,
to_char(c.week_start, 'mm/dd/yyyy')
|| ' - ' || to_char(c.week_start + 6, 'mm/dd/yyyy') week_of_year,
1 as active
from your_table yt
inner join (select distinct trunc(cl.calendar_date, 'iw') week_start
from calendar cl) c on c.week_start <= nvl(yt.enddate, SYSDATE) AND c.week_start + 6 >= yt.startdate
order by yt.member,
c.week_start;
MEMBER STARTDATE ENDDATE WEEK_OF_YEAR ACTIVE
------ ---------- ---------- ----------------------- ----------
A 01/31/2017 01/30/2017 - 02/05/2017 1
A 01/31/2017 02/06/2017 - 02/12/2017 1
A 01/31/2017 02/13/2017 - 02/19/2017 1
A 01/31/2017 02/20/2017 - 02/26/2017 1
B 02/01/2017 02/15/2017 01/30/2017 - 02/05/2017 1
B 02/01/2017 02/15/2017 02/06/2017 - 02/12/2017 1
B 02/01/2017 02/15/2017 02/13/2017 - 02/19/2017 1
Like Alex, I've assumed your null enddate runs up until today (sysdate). However, looking at your results for member B, it looks like you're looking for an overlapping range (since 30th Jan is not between 1st and 15th Feb), so I've amended my join clause accordingly. This results in an extra row for member A, so maybe you're wanting to run null enddates up until the previous Sunday of sysdate? Not sure. I'm sure you'll be able to amend that yourself, if you need to.

how to join all the tables without using any condition

all the below results are not related to each other wheras we cannot use any condition.
ID
----------
1
2
3
4
5
6
7
7 rows selected.
NAME
-----------------
SRUJAN
DEERAJ
VINEETH
CHANIKYA
LAVANYA
KAVITHA
BUNNY
7 rows selected.
AGE
----------
23
24
26
25
29
28
24
7 rows selected.
ADDRESS
-------------
NAGARAM
BANDLAGUDA
UPPAL
KUKATPALLY
HB COLONY
MOULALI
BOUDHA NAGAR
7 rows selected.
SALARY
----------
12000
13000
14000
15000
16000
17000
18000
7 rows selected.
I USED
SQL>select id,name,age,address,salary from table1,table2,table3,table4,table5;
but it showing 16807 rows selected
i want to get only one table.
please suggest a query.
The only possible join between 2 tables having only 1 columns and both having no relation is cross join. You cannot avoid it. And the same you are getting when you tried to join. The best way for you is to create a sequence as ID and then call it in your select statement of table2.
CREATE SEQUENCE TEST_SEQ
START WITH 1
MAXVALUE 9999999999999999999999999999
MINVALUE 1
NOCYCLE;
select TEST_SEQ.nextval ID,col1 NAME
from table2;
Here is one way, using inner joins. This solution matches the lowest id with the first name in alphabetical order, the lowest age, etc.
If instead of this ordered matching you need random matching, that is easy to do as well: in the over... clause of the prep tables, change the order by clause to order by dbms_random.value() (in all places).
In the solution below I use only the first three tables, but the same works for any number of input tables.
with
tbl_id ( id ) as (
select 1 from dual union all
select 2 from dual union all
select 3 from dual union all
select 4 from dual union all
select 5 from dual union all
select 6 from dual union all
select 7 from dual
),
tbl_name ( name ) as (
select 'SRUJAN' from dual union all
select 'DEERAJ' from dual union all
select 'VINEETH' from dual union all
select 'CHANIKYA' from dual union all
select 'LAVANYA' from dual union all
select 'KAVITHA' from dual union all
select 'BUNNY' from dual
),
tbl_age ( age ) as (
select 23 from dual union all
select 24 from dual union all
select 26 from dual union all
select 25 from dual union all
select 29 from dual union all
select 28 from dual union all
select 24 from dual
),
prep_id ( id, rn ) as (
select id, row_number() over (order by id) from tbl_id
),
prep_name ( name, rn ) as (
select name, row_number() over (order by name) from tbl_name
),
prep_age ( age , rn ) as (
select age, row_number() over (order by age) from tbl_age
)
select i.id, n.name, a.age
from prep_id i inner join prep_name n on i.rn = n.rn
inner join prep_age a on i.rn = a.rn
;
Output:
ID NAME AGE
---------- -------- ----------
1 BUNNY 23
2 CHANIKYA 24
3 DEERAJ 24
4 KAVITHA 25
5 LAVANYA 26
6 SRUJAN 28
7 VINEETH 29
7 rows selected

Increase row number every time a flag changes

I have gone through a similar post in Stack overflow...
but my query is :
If my table generates a flag in run time execution,then how can I increase Grp_number(generate run time) every time my flag changes.
my Oracle query:
Select emp_id,
Case when MOD(rownum/3)=1 and rownum>1 then 'Y' else 'N' as flag
from Transaction_table
Desired o/p Data format:
emp_id Flag GRP_number
1 N 1
2 N 1
3 N 1
4 Y 2
5 N 2
6 N 2
7 Y 3
You cannot reference a column in another column in the same select list. You need to use sub query to avoid INVALID IDENTIFIER error.
Do it like -
WITH DATA AS(
SELECT emp_id,
CASE
WHEN MOD(rownum/3)=1
AND rownum >1
THEN 'Y'
ELSE 'N' AS flag
FROM Transaction_table
)
SELECT emp_id, flag, SUM(gap) over (PARTITION BY person
ORDER BY DAY) grp
FROM(
SELECT emp_id, flag,
CASE WHEN flag = lag(flag) over (PARTITION BY person
ORDER BY DAY)
THEN 0
ELSE 1
END gap
FROM DATA)

using vb6 and oracle

I have the records using the query
SELECT trunc(createdon,'hh') CREATEDON,count(*)
FROM WHERE LABSTATUS=1 AND
CREATEDON >=TO_DATE('01/07/2010 10','DD/MM/YYYY hh')
GROUP BY trunc(createdon,'hh')
in an hourly basis.. I need to place the count value in corresponding time column in the grid.
How can I do?? any idea
You can do it like this:
Hours are in columns
SELECT TRUNC(createdon,'hh'), SUM(CASE WHEN hh=1 THEN 1 ELSE 0) h1,
SUM(CASE WHEN hh=2 THEN 1 ELSE 0) h2 .....
FROM TABLle1,
(SELECT 1 AS hh FROM dual
UNION
SELECT 2 AS hh FROM dual
UNION
SELECT 3 AS hh FROM dual
....
) hours
WHERE LABSTATUS=1 AND CREATEDON >=TO_DATE('01/07/2010 10','DD/MM/YYYY hh') AND
TRUNC(createdon,'hh')= hours.hh
GROUP BY TRUNC(createdon,'hh')
Hours are in rows:
SELECT hours.hh, SUM(CASE WHEN TRUNC(createdon,'hh')= hours.hh THEN 1 ELSE 0 END) hh,
FROM TABLle1,
(SELECT 1 AS hh FROM dual
UNION
SELECT 2 AS hh FROM dual
UNION
SELECT 3 AS hh FROM dual
....
) hours
WHERE LABSTATUS=1 AND CREATEDON >=TO_DATE('01/07/2010 10','DD/MM/YYYY hh')
GROUP BY hours.hh
This query will result like this
row 1 12
row 2 0
row 3 4
...

Resources