Row Count in Trailer Record - sql

I need to create a text file that contains a header row, multiple data rows (could be any number of rows once I put it to work), and a trailer row that includes the final row number, which is to be the number of data rows, plus 2 (the header and trailer).
I have been able to achieve the desired result with the following query, but I wonder if there is a more efficient way of handling the task?
My test query:
--SINGLE HEADER ROW
SELECT 'PH3', to_char('10000000')
FROM DUAL
UNION ALL
--MULTIPLE DATA ROWS
SELECT 'PD3', 'NO SSN'
FROM students s
WHERE s.schoolid = '999999'
UNION ALL
--SINGLE TRAILER ROW
SELECT 'PT3', to_char(count(*)+2)
FROM students s
WHERE s.schoolid = '999999'

I have been able to achieve the desired result with the following query, but I wonder if there is a more efficient way of handling the task?
As far as I know, it is not documented anywhere than UNION ALL will generate rows in any particular order. Even if it appears today to always work as you expected.
As of myself, I would consider than, without a specific ORDER BY clause, any set operation will produce an unordered result.
So for the sake of your peace of mind (or of some future maintainer), I would go for an explicit ordering. Probably not more efficient, but guarantying to output your rows in the expected order:
SELECT "col1", "col2" FROM (
SELECT 0 as "position", 'PH3' as "col1", to_char('10000000') as "col2"
FROM DUAL
UNION ALL
--MULTIPLE DATA ROWS
SELECT 1, 'PD3', 'NO SSN'
FROM students s
WHERE s.schoolid = '999999'
UNION ALL
--SINGLE TRAILER ROW
SELECT 2, 'PT3', to_char(count(*)+2)
FROM students s
WHERE s.schoolid = '999999'
) ORDER BY "position";
And, if you really want to get rid of the two SELECT having the same WHERE clause, maybe you could use a CTE?
WITH data AS (SELECT 'NO SSN'
FROM students s
WHERE s.schoolid = '999999')
SELECT "col1", "col2" FROM (
SELECT 0 as "position", 'PH3' as "col1", to_char('10000000') as "col2"
FROM DUAL
UNION ALL
--MULTIPLE DATA ROWS
SELECT 1, 'PD3', 'NO SSN'
FROM data
UNION ALL
--SINGLE TRAILER ROW
SELECT 2, 'PT3', to_char(count(*)+2)
FROM data
) ORDER BY "position";

You can use ROWNUM to obtain the final count of rows produced by the query, as in the following:
SELECT rectype,
case
when rectype <> 'PT3' THEN ssn
else to_char(rownum, 'TM9')
end as ssn
FROM
(--SINGLE HEADER ROW
SELECT 'PH3' as rectype, to_char('10000000') as ssn
FROM DUAL
UNION ALL
--MULTIPLE DATA ROWS
SELECT 'PD3' as rectype, 'NO SSN' as ssn
FROM students s
WHERE s.schoolid = '999999'
UNION ALL
--SINGLE TRAILER ROW
SELECT 'PT3' as rectype, NULL as ssn
FROM DUAL)
SQLFiddle here
Share and enjoy.

Related

OracleSQL CASE Statement on Number in string

I have a table called TEST_TABLE with 1 column called COLUMN1. This table has 2 records:
V.WEEKLY_2020_15
V.WEEKLY_2020_16
I'm trying to write a CASE statement that maps these records to different Periods. e.g.
SELECT
CASE WHEN COLUMN1='V.WEEK_2020_ **MAXIMUM NUMBER** ' THEN 'CURRENT PERIOD'
ELSE 'HISTORICAL PERIOD 1' END
FROM TEST_TABLE
I'm not sure what is the best way to do this though. I need to get the number from the end of the string, and then compare it to the other numbers in the table. Once it finds one number that is higher or lower it can stop the search as there will always only be 2 numbers in this table.
You can get the number from the end of the string with a regular expression. This one gets the 3rd group of characters which don't include an underscore.
select column1, regexp_substr(column1,'[^_]+',1,3) from test_table;
Alternately you could get the 2nd group of numbers with regexp_substr(column1,'[0-9]+',1,2). The best regexp will depend on your knowledge of the possible string values. If you know the number will always be the last 2 characters, you could do substr(column1, -2)
And if you want to identify rows which have the highest/lowest/etc value, adding a column which applies an window/analytical function is a common pattern.
-- sample data
with test_table as (select 'V.WEEKLY_2020_15' as column1 from dual
union select 'V.WEEKLY_2020_16' from dual)
-- query
SELECT column1, regexp_substr(column1,'[^_]+',1,3) as regex, max_number,
CASE WHEN COLUMN1=max_number THEN 'CURRENT PERIOD'
ELSE 'HISTORICAL PERIOD 1' END as period
FROM (select test_table.*,
max(column1) over (order by regexp_substr(column1,'[^_]+',1,3) desc) as max_number
from test_table) T;
Usually the data will be more complicated then you're showing - for example, you might have 2 periods in the table for each primary key, and then you'll want to partition your window function.
-- sample data
with test_table as (select 1 as pk, 'V.WEEKLY_2020_15' as column1 from dual
union select 1, 'V.WEEKLY_2020_16' from dual
union select 2, 'V.WEEKLY_2021_1' from dual
union select 2, 'V.WEEKLY_2021_200' from dual)
-- query
SELECT pk, column1, regexp_substr(column1,'[^_]+',1,3) as regex,
CASE WHEN COLUMN1=max_number THEN 'CURRENT PERIOD'
ELSE 'HISTORICAL PERIOD 1' END as period
FROM (select test_table.*,
max(column1) over (partition by pk order by regexp_substr(column1,'[^_]+',1,3) desc) as max_number
from test_table) T;
Output:
PK
COLUMN1
REGEX
PERIOD
1
V.WEEKLY_2020_16
16
CURRENT PERIOD
1
V.WEEKLY_2020_15
15
HISTORICAL PERIOD 1
2
V.WEEKLY_2021_200
200
CURRENT PERIOD
2
V.WEEKLY_2021_1
1
HISTORICAL PERIOD 1

Excluding a row that contains a specific value

I want to exclude people who have joined a specific group. For example, if some students signed up for an Orchestra club, and I want to retrieve a list of students who did NOT sign up for orchestra, how do I do so?
I am unable to simply do a Group By clause because some students may have joined multiple clubs, and would bypass the Where condition and still show up in the query,
as shown here.
I am thinking about using a CASE statement in the SELECT clause to flag the person as '1' if they have joined Orchestra, and '0' if they have not, but I'm struggling to write an aggregate CASE function, which would cause issues from the GROUP BY clause.
Any thoughts on how to flag people with a certain row value?
Apparently my table didn't get saved onto SQLFiddle so you can paste the code below on your own screen:
CREATE TABLE activity ( PersonId, Club) as
select 1, 'Soccer' from dual union
select 1, 'Orchestra' from dual union
select 2, 'Soccer' from dual union
select 2, 'Chess' from dual union
select 2, 'Bball' from dual union
select 3, 'Orchestra' from dual union
select 3, 'Chess' from dual union
select 3, 'Bball' from dual union
select 4, 'Soccer' from dual union
select 4, 'Bball' from dual union
select 4, 'Chess' from dual;
Use the HAVING clause instead of using WHERE, with case expression :
HAVING max(case when column = ‘string’ then 1 else 0 end) = 0
Add this after your group by .
How about selecting a list of user ids from the activity table and excluding it:
SELECT * FROM users WHERE id NOT IN
(SELECT PersonId FROM activity WHERE Club = 'Orchestra');
You could use a subquery to return a list of people to exclude.
-- Returns person 2 and 4.
SELECT
PersonId
FROM
activity
WHERE
PersonId NOT IN
(
-- People to exclude.
SELECT
PersonId
FROM
activity
WHERE
Club = 'Orchestra'
)
GROUP BY
PersonId
;
EDIT Removed superfluous distinct in subquery - thanks #mathguy.
select * from
(
select a.*, case when Club ='Orchestra' then 1 else 0 end flag
from activity a
) where flag =1; --> get some students signed up for an Orchestra club
select * from
(
select a.*, case when Club ='Orchestra' then 1 else 0 end flag
from activity a
) where flag =0; --> get students not signed up for an Orchestra club

Oracle SQL Min in Select Clause

Can some one please help me in writing a sql query that should do a oracle min function based on the following conditions.
For eg for column values
0,0,0,0 then output should be 0
0,null,0,null then output should be o
0,2,4,5,6 then output should be 2 (Note that we are excluding Zero here)
0,2,null,4,5 then output should be 2 (same here we are excluding zero)
null,null,null, null then output should be null.
I wrote query already that satisfies all the above cases but failing for last case when all the column values are null. Instead of returning null it is returning 0. Can some one modify the below query to fit for the last case as well?
select NVL(MIN(NULLIF(columnname,0)),0) from tablename;
Please also keep in mind that the query should be runnable in oracle as well as hsqldb as we are using hsql db for running junits.
If all 4 cases satisfied by your query then just a case will solve your problem.
SELECT CASE WHEN MIN(COLUMNNAME) IS NULL THEN NULL ELSE NVL(MIN(NULLIF(COLUMNNAME,0)),0) END FROM TABLENAME;
Note:- assuming all the cases satisfied by your query except 5th.
I will show below an input table with two columns, ID and VAL, to illustrate the various possibilities. You want a single result per ID (or even for the entire table), so this must be a job for GROUP BY and some aggregate function. You want to distinguish between three types of values: Greater than zero, zero, and null (in this order); you want to pick the "highest priority group" that exists for each ID (in this order of priority), and for that priority group only, you want to pick the min value. This is exactly what the aggregate FIRST/LAST function does. To order by the three "classes" of values, we use a CASE expression in the ORDER BY clause of the aggregate LAST function.
The WITH clause below is not part of the solution - I only include it to create test data (in your real life situation, use your actual table and column names and remove the entire WITH clause).
with
inputs ( id, val ) as (
select 1, 0 from dual union all
select 1, 0 from dual union all
select 1, 0 from dual union all
select 2, 0 from dual union all
select 2, null from dual union all
select 2, 0 from dual union all
select 3, 0 from dual union all
select 3, 2 from dual union all
select 3, 5 from dual union all
select 4, 0 from dual union all
select 4, 3 from dual union all
select 4, null from dual union all
select 5, null from dual union all
select 5, null from dual
)
select id,
min(val) keep (dense_rank last order by case when val > 0 then 2
when val = 0 then 1
else 0
end
) as min_val
from inputs
group by id
order by id
;
ID MIN_VAL
---------- ----------
1 0
2 0
3 2
4 3
5

why are the results of variable and condition are different?

I was wondering to figure out this problem.
In the below query am trying to select #transcript as the combination of 2 column values. but when i am using this variable in select statement am getting only 3 results (actual output should be 8 results). where as when i use the condition directly in select statement am getting actual output.
can anybody please help me in figuring out this issue.
declare #transcript varchar(10)
select #transcript = [CAREER_CD]+[CAREER_SUFX_CD] from dbo.SR0DAT
select DISTINCT #transcript transcriptCareerCode,
case #transcript
when 'U1' then 'BACCALAUREATE'
when 'U2' then 'SECOND BACCALAUREATE'
when 'G1' then 'GRADUATE'
when 'L1' then 'LAW'
when 'D1' then 'DENTISTRY'
when 'M1' then 'MEDICINE'
when 'IU' then 'transcriptCareerName'
when 'IG' then 'IEO Graduate'
end as transcriptCareerName
from dbo.SR0DAT
WHERE #transcript <>'G2'
union
select 'IU','IEO Undergraduate'
union
select 'IG','IEO Graduate'
output:
transcriptCareerCode transcriptCareerName
G1 GRADUATE
IG IEO Graduate
IU IEO Undergraduate
2nd code:
select DISTINCT [CAREER_CD]+[CAREER_SUFX_CD] transcriptCareerCode,
case [CAREER_CD]+[CAREER_SUFX_CD]
when 'U1' then 'BACCALAUREATE'
when 'U2' then 'SECOND BACCALAUREATE'
when 'G1' then 'GRADUATE'
when 'L1' then 'LAW'
when 'D1' then 'DENTISTRY'
when 'M1' then 'MEDICINE'
when 'IU' then 'transcriptCareerName'
when 'IG' then 'IEO Graduate'
end as transcriptCareerName
from dbo.SR0DAT
WHERE [CAREER_CD]+[CAREER_SUFX_CD] !='G2'
union
select 'IU','IEO Undergraduate'
union
select 'IG','IEO Graduate'
output:
transcriptCareerCode transcriptCareerName
D1 DENTISTRY
G1 GRADUATE
IG IEO Graduate
IU IEO Undergraduate
L1 LAW
M1 MEDICINE
U1 BACCALAUREATE
U2 SECOND BACCALAUREATE
While both queries are indeed run against your table none of its columns are being used at all in the first one: for each row you're simply returning a fixed/constant expression. The distinct option is helping to mask what's going on and if you remove the that you'll see all of the multiple copies that are being collapsed into one row. If you also say select #transcript, * ... you'll see where the rest of the data is as well.
Are you just trying to accomplish an alias for the transcript expression so you don't have to repeat it?
with T as (
select CAREER_CD + CAREER_SUFX_CD as transcript
from dbo.SR0DAT
)
select DISTINCT
transcript transcriptCareerCode,
case transcript
when 'U1' then 'BACCALAUREATE'
when 'U2' then 'SECOND BACCALAUREATE'
when 'G1' then 'GRADUATE'
when 'L1' then 'LAW'
when 'D1' then 'DENTISTRY'
when 'M1' then 'MEDICINE'
when 'IU' then 'transcriptCareerName'
when 'IG' then 'IEO Graduate'
end as transcriptCareerName
from T
where transcript <> 'G2'
union
select 'IU', 'IEO Undergraduate'
union
select 'IG', 'IEO Graduate'
In your first query you are creating a singular value through the variable #transcript. As a result, your call to:
select #transcript = [CAREER_CD]+[CAREER_SUFX_CD] from dbo.SR0DAT
Only guarantees that the last record in the SELECT statement gets assigned to the #transcript variable.
As a result, only one row from that query will return (you've union'ed the other two rows in your first example).
The second example is using actual database set logic to pull in values across the set - not just the last value in the set.

How to return a record for every item in the 'where value in ' clause?

Oracle11g
I want to know if a player has ever played shortstop. However, a player
may not even be in my table, yet I'd still like to return a row for that player. In this case, player #3 is not in table, but I'd like to return a row nevertheless.
Selection Criteria
If player has at least one SHORTSTOP record then return just 1 row with YES.
If player has at least one record and none of them are SHORTSTOP then return just one row with NO.
If player has no records then return just one row with NO.
Query
with baseball_players as
(select 1 as player_id, 'SHORTSTOP' as position from dual union all
select 1 as player_id, 'FIRSTBASE' as position from dual union all
select 2 as player_id, 'FIRSTBASE' as position from dual)
select player_id, case position
when 'SHORTSTOP' then 'YES'
else 'NO'
end has_played
from baseball_players
where player_id in (1,2,3)
Question: How can I write query to get desired results?
Desired Output
PLAYER_ID HAS_PLAYED
----------------------
1 YES
2 NO
3 NO
How about this:
WITH playerList AS
(
SELECT
1 player_id
FROM
dual
UNION
SELECT
2
FROM
dual
UNION
SELECT
3
FROM
dual
)
, baseball_players as
(
select 1 as player_id, 'SHORTSTOP' as position from dual union all
select 1 as player_id, 'FIRSTBASE' as position from dual union all
select 2 as player_id, 'FIRSTBASE' as position from dual
)
SELECT
pl.player_id
, MAX(CASE WHEN bp.position = 'SHORTSTOP' THEN 'YES' ELSE 'NO' END)
FROM
playerList pl
LEFT JOIN
baseball_players bp
ON
bp.player_id = pl.player_id
GROUP BY
pl.player_id
Rather than using a where clause, create a tempoary table, insert the values in and left join on the table you created.
Use a case statement for IF NULL then NO, otherwise YES.