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.
Related
I'm configuring an integration tool which moves data from System A to System B. I provide an SQL SELECT statement which it runs against System A, and the statement's output determines what in System B gets updated. For instance, to update Team 1's international sales figure:
SELECT
'Team1_Int_Sales_Count' Code,
count(*) Count,
to_char(<integration tool date syntax>) Period
FROM
Sales
JOIN
ADozenMoreTables ON stuff
WHERE
ADozenOpaqueFields IN ADozenOpaqueReferences
AND Sales.Date BETWEEN <integration tool date syntax>
AND Team = 'Team1'
AND Sales.Type = 'International'
It uses the returned fields Code, Value, and Period to update a that code for that period with that value. (Fields identified by what order they're in, not by name.)
Then, for Team 1's domestic sales, I've copied and pasted the entire query, and changed the code and one WHERE clause:
SELECT
'Team1_Dom_Sales_Count' Code,
<otherwise as above>
AND Sales.Type = 'Domestic'
And then I copy and paste it four more times for Teams 2 and 3
SELECT
'Team2_Int_Sales_Count' Code,
<snip>
AND Team = 'Team2'
AND Sales.Type = 'International'
SELECT
'Team2_Dom_Sales_Count' Code,
<snip>
AND Team = 'Team2'
AND Sales.Type = 'Domestic'
SELECT
'Team3_Int_Sales_Count' Code,
<snip>
AND Team = 'Team3'
AND Sales.Type = 'International'
SELECT
'Team3_Dom_Sales_Count' Code,
<snip>
AND Team = 'Team3'
AND Sales.Type = 'Domestic'
The full problem has a ~60-line SELECT statement with 3x3x3 permutations and the amount of copy/pasting involved is giving me the fear.
Is there some way I can write a single SQL SELECT statement which will step through all the permutations without the copy-pasted repetition? In my mind's eye I'd have the permutations in a temporary table created inline, or as a 2-dimensional array, and the query could return the code and the value where the other two fields match:
{ {'Team1_Int_Sales_Count', 'Team1', 'International'},
{'Team1_Dom_Sales_Count', 'Team1', 'Domestic'},
{'Team2_Int_Sales_Count', 'Team2', 'International'},
{'Team2_Dom_Sales_Count', 'Team2', 'Domestic'},
{'Team3_Int_Sales_Count', 'Team3', 'International'},
{'Team3_Dom_Sales_Count', 'Team3', 'Domestic'} } Permutations
Constraints here are the integration tool requires I provide each task with a single SELECT statement. I cannot preface it with a WITH statement, or declare functions, or store the complex query as a view within the source database, or do anything that's fun or nice. And it's an Oracle ODBC connection, so it uses Oracle SQL.
You seem to want a cross join.
SELECT t.team || ct.code code,
t.team,
ct.type
FROM (SELECT '_Int_Sales_Count' code,
'International' type
FROM dual
UNION ALL
SELECT '_Dom_Sales_Count' code,
'Domestic' type
FROM dual) ct
CROSS JOIN (SELECT 'Team1' team
FROM dual
UNION ALL
SELECT 'Team2' team
FROM dual
UNION ALL
SELECT 'Team3' team
FROM dual) t;
db<>fiddle
A table of permutations can be constructed like this:
SELECT
Permutations.Code AS Code,
COUNT(*) AS Count,
...
JOIN (SELECT * FROM (
(SELECT 'Team1_Int_Sales_Count' AS Code, 'Team1' AS Team, 'International' AS Type FROM Dual),
UNION (SELECT 'Team1_Dom_Sales_Count' AS Code, 'Team1' AS Team, 'Domestic' AS Type FROM Dual),
UNION (SELECT 'Team2_Int_Sales_Count' AS Code, 'Team2' AS Team, 'International' AS Type FROM Dual),
UNION (SELECT 'Team2_Dom_Sales_Count' AS Code, 'Team2' AS Team, 'Domestic' AS Type FROM Dual),
UNION (SELECT 'Team3_Int_Sales_Count' AS Code, 'Team3' AS Team, 'International' AS Type FROM Dual),
UNION (SELECT 'Team3_Dom_Sales_Count' AS Code, 'Team3' AS Team, 'Domestic' AS Type FROM Dual)
)) AS Permutations
ON Permutations.Team = Sales.Team AND Permutations.Type = Sales.Type
WHERE...
I would do it like:
with
team as (select level lv from dual connect by level <= 3)
, bas as (select '_Int_Sales_Count' n_count, 'International' type from dual
union all select '_Dom_Sales_Count' n_count, 'Domestic' type from dual)
, permutations as
( select 'Team' || lv ||n_count as code, 'Team' || lv as team, type from team join bas on (1=1))
select * from permutations
but with the constraints:
select 'Team' || lv ||n_count as code, 'Team' || lv as team, type from
(select level lv from dual connect by level <= 3) team,
(select '_Int_Sales_Count' n_count, 'International' type from dual
union all select '_Dom_Sales_Count' n_count, 'Domestic' type from dual) bas
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
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.
When I add a new argument to the SELECT clause of a UNION, I get more records... how can this be? Isn't a UNION just mashing them together? Example:
EDIT: They're absolutely distinct. the code column is either "IN" or "OUT", and that's what I'm using to separate the two.
EDIT2: UNION ALL gives me 80 records, like it should, but it's odd because my two SELECT statements are absolutely distinct.
FINAL EDIT: Ultimate problem was records within one of my SELECT statements being not DISTINCT, not between the two SELECT statements. Thanks all.
-- Yields 76 records
SELECT
f.date
, f.code
, f.cost
FROM a.fact f
WHERE f.code = 'IN'
UNION
SELECT
f2.date
, f2.code
, f2.cost
FROM a.fact2 f2
WHERE f2.code = 'OUT'
;
-- Yields 80 records
SELECT
f.key
, f.date
, f.code
, f.cost
FROM a.fact f
WHERE f.code = 'IN'
UNION
SELECT
f2.key
, f2.date
, f2.code
, f2.cost
FROM a.fact2 f2
WHERE f2.code = 'OUT'
;
change UNION to UNION ALL and you should get the same results. UNION selects distinct rows, UNION ALL should select all.
By default UNION selects distinct results, there must be duplicates between your result sets.
The union all will solve your problem, as mentioned in a previous answer, it selects the distinct values only
References Oracle docs.
I'm working on this problem for several days. I have a oracle database.
The problem must be resolved in one query. No Function, Pocedure, ...
I want to make a select. When he has results, post them. Else there should be "empty result".
select case
when count(*) = 0
then 'no Entry'
else MAX(Member)--all Members should be here
END as Member
from tableMember
where Membergroup = 'testgroup';
The problem is that Oracle wants an Agregat function by the else. So I only get one value if the result is not "no entry". I need all values.
Everybody who can help me is welcome and makes me happy.
not sure what do you try to achieve, perhaps this
select member from tablemember where Membergroup = 'testgroup'
union
select 'no Entry'
from dual
where NOT EXISTS ( select member from tablemember where membergroup = 'testgroup')
;
There's no need for two aggregate queries, you just need to check whether max(member) is null. I'd do it this way to make it clear what's going on.
select case when max_member is null then 'no entry' else max_member end as member
from ( select max(member) as max_member
from tablemember
where membergroup = 'testgroup'
)
If, however, you want to return all members you can do something like the following:
select member
from tablemember
where membergroup = 'testgroup'
union all
select 'no entry'
from dual
where not exists ( select 1 from tablemember where membergroup = 'testgroup')
If you RIGHT JOIN your query with a query for the empty set you will always get one row and will get all the rows if your query returns data. This is less expensive (faster) than a UNION or UNION ALL with a NOT EXISTS because it does not require multiple scans of the data.
SELECT nvl(a.member,b.member) member
FROM (SELECT member FROM tablemember WHERE membergroup='????') a
RIGHT JOIN (SELECT 'no Entry' member FROM dual) b ON 1=1;
Test Environment:
DROP TABLE tablemember;
CREATE TABLE tablemember AS
(
SELECT TO_CHAR(level) member
, DECODE(mod(level, 5), 0, 'testgroup', 'othergroup') membergroup
FROM dual CONNECT BY level <= 50
);
You can use some aggregate functions and NVL for achieve you goal:
SELECT MIN('VALUE 1') AS p1, MIN('VALUE 2') AS p2 FROM DUAL WHERE 1=0
result of this query is:
NULL, NULL
next, replace empty values by desired strings:
SELECT
NVL(MIN('1'), 'empty value 1') AS p1,
NVL(MIN('STRING VALUE'), 'empty value 2') AS p2,
NVL(MIN((select 'subquery result' from dual)), 'empty subquery result') as p3
FROM
DUAL
WHERE
1=0
But, you can't mix numbers and strings in fields.
Try this:
DECLARE C INTEGER;
SELECT COUNT(*) INTO C FROM tableMember WHERE Membergroup = 'testgroup';
IF C > 0
THEN
SELECT * FROM tableMember;
ELSE
SELECT 'No results!' FROM tableMember;
END IF;