I'm having a brain fart and I can't seem to push past it. I've done this before but I can't remember how I did it. Here's the gist: I have a table I created (test) and in this table I'm storing some random information. (It's a test table and so the information in the table is not applicable to anything). My test table looks like this:
Name varchar2 Primary key
age varchar2
sex char
social varchar2 Primary Key
I'm using this table as an example and that is why I have the two primary keys. I was showing a student how to do a composite key.
My question is this: how do I create a query that has a total count at the end of the rows?
I'm looking for something like this:
Name Male Female
xxxxx m
xxxxx m
xxxxx m
xxxxx m
xxxxx F
xxxxx F
Total: 4 2
I created the following query (which doesn't quite work) just as a test.
set feedback on
compute sum(COUNT(SEX)) LABEL "# of males" of sex_count on report
select count(sex) as sex_count
from test
where sex = upper('m')
Would this resolve your problem?
select name, male, female from (
select name,
decode( sex, 'm', 'm', null) Male,
decode( sex, 'f', 'f', null) Female, 0 n
from test
union
select 'Total',
to_char(sum(decode(sex, 'm', 1, 0))),
to_char(sum(decode(sex, 'f', 1, 0))),
1
from test
group by 'Total'
) order by n
Link to Fiddle:
http://sqlfiddle.com/#!4/0e501/3/0
Based on the "compute...on report" statement, it looks like you're using SQL*Plus. Try this:
compute count label total of male female on report
break on report
select name, decode(sex, 'm', sex) male, decode(sex, 'f', sex) female
from test
order by name;
Related
I'm having this problem we have this database witch IDS are stored in varchar2 type this ids contains Letters.
Is there any solution to convert a string to a number no matter what the value if this string.
for example there is : SELCT ASCII('t') FROM DUAL; result : 116.
but ASCII accept only one CHAR Hope you get the idea. sorry for my english
use oracle translate method to replace A-Z or a-z characters with numbers.
then use to_number to get number from it.
select translate('A1B2C3', 'ABC', '456') from dual; --result '415263'
select to_number(translate('A1B2C3', 'ABC', '456')) from dual; --result 415263
translate function documentation
The Oracle/PLSQL TRANSLATE function replaces a sequence of characters in a string with another set of characters. However, it replaces a single character at a time.
For example, it will replace the 1st character in the string_to_replace with the 1st character in the replacement_string. Then it will replace the 2nd character in the string_to_replace with the 2nd character in the replacement_string, and so on.
EDIT: After discussing further with the OP, it turns out he needed a function (in the mathematical sense) from short strings to integers. Such a function is ORA_HASH. The OP decided that ORA_HASH is likely what is needed for his project.
https://docs.oracle.com/cd/B28359_01/server.111/b28286/functions112.htm#SQLRF06313
The solution below is kept for historical perspective.
You could use the analytic function DENSE_RANK to assign numbers to strings.
For example:
with
employees ( id, first_name, last_name ) as (
select 'ABC', 'Jane', 'Smith' from dual union all
select 'ABD', 'Jane', 'Dryer' from dual union all
select 'XYZ', 'Mike', 'Lopez' from dual
)
-- End of simulated inputs (for testing purposes only).
-- Solution (SQL query) begins below this line.
select id, dense_rank() over (order by id) as num_id, first_name, last_name
from employees
;
ID NUM_ID FIRST_NAME LAST_NAME
--- ------ ---------- ---------
ABC 1 Jane Smith
ABD 2 Jane Dryer
XYZ 3 Mike Lopez
I have this query written by someone else and I am trying to figure out how is it working. I have general idea about all these things such as row_number() , partition by, pivot but I am unable to understand them all together.
For this query :
select
d, p, s, a
from
(
select name,occupation, (ROW_NUMBER() OVER (partition by occupation order by name)) as rownumber from occupations
)
pivot
(
max(name)
for occupation
in ('Doctor' as d, 'Professor' as p, 'Singer' as s, 'Actor' as a)
)
order by rownumber;
This is the input table on which the above query works :
This it the output generated by the query which is correct as per the question :
Jenny Ashley Meera Jane
Samantha Christeen Priya Julia
NULL Ketty NULL Maria
Now, I want to know how the output is generated by the query i.e. step by step with flow of execution. Explanation with easy examples matching the above situation would be much appreciated. Thanks in advance.
After from clause you have following :
select name,occupation, (ROW_NUMBER() OVER (partition by occupation order by name))
Above virtually restack your table data in three columns - Name, occupation, rownumber. rownumber will reset itself as soon as occupation column changes. Output data will be like :
NAME OCCUPATION ROWNUMBER
-------------------- -------------------- ----------
Jane ACTOR 1
Julia ACTOR 2
Maria ACTOR 3
JENNY DOCTOR 1 <-- rownumber reset to 1
Sammantha DOCTOR 2
Pivot function let you aggregate result & rotate rows into columns.
Pivot usage code is :
PIVOT
(
aggregate_function(column2)
FOR column2
IN ( expr1, expr2, ... expr_n) | subquery
)
So your PIVOT function have name stacked NAME based on OCCUPATION . Each stack (column in output) is ordered by rownumber column inserted via first subquery.
For Sample purpose lets create a table with below schema and fill some sample values
CREATE TABLE games(ID INT ,Name VARCHAR(20));
INSERT INTO games(ID,Name) VALUES (2008,'Beijing');
INSERT INTO games(ID,Name) VALUES (2012,'London');
INSERT INTO games(ID,Name) VALUES (2012,12);
INSERT INTO games(ID,Name) VALUES (2012,654);
Output:
ID NAME
2008 Beijing
2012 London
2012 12
2012 654
In the above table we have both number and string data in the name column lets write a query that uses the REGX to filter only the numerical rows
SELECT TO_NUMBER(Name)as Trimmed FROM games where REGEXP_LIKE(Name, '(?<=\s|^)\d+(?=\s|$)', '')
Output:
TRIMMED
12
654
Now here is the problem if write a where clause of getting values greater than 12 from the above result it throws invalid number.
Select * from (SELECT TO_NUMBER(Name)as Trimmed FROM games where REGEXP_LIKE(Name, '(?<=\s|^)\d+(?=\s|$)', '')) T1 where T1.Trimmed >12 ;
I found this is how the oracle query planning works but is there any other way i can achieve this
This will work:
Select * from
(SELECT Name as Trimmed
FROM games where REGEXP_LIKE(Name, '(?<=\s|^)\d+(?=\s|$)', '')) T1
where to_number(T1.Trimmed) >12 ;
Unfortunately you need a subquery. It can't be done with one where.
This can be done in a single query:
with
inputs as (
select 2008 as id, 'Beijing' as name from dual union all
select 2012 , 'London' from dual union all
select 2012 , '12' from dual union all
select 2012 , '654' from dual
)
select id, name
from inputs
where translate(name, 'a0123456789', 'a') is null
and to_number(regexp_replace(name, '[^[:digit:]]', '')) > 12
;
ID NAME
---------- -------
2012 654
1 row selected.
regexp_replace removes all the characters except digits, so the test can be done regardless of what the name is. If there are no digits in the name, the result is NULL, which can be converted to number (it is still null).
The translate solution for testing for "all-digits" is more efficient than using regexp_like. The odd-looking 'a' in translate is needed due to an oddity in the translate function itself (see the documentation). This test is not needed if all the names are either "all letters" or "all digits" (if "all letters", the second test with regexp_replace would suffice); but the first test is needed if names like "Sydney 2000" are possible in the name column.
Using oracle database and the table is like this
id sdate offer
1 16-04-13 offer1
2 11-04-13 offer2
3 21-04-13 offer4
4 31-03-13 offer5
5 14-04-13 offer8
6 10-04-13 offer4
7 15-04-13 offer1
I want to calculate weeks as w1 w2 and so on from the sysdate.
Example:
for id=1, sdate falls on 16-04-13 so it is week1.
for id=5, sdate falls on 14-04-13 so it is week2
The result should be like this
id week offer
1 w1 offer1
5 w2 offer8
.............
To treat today and the prior six days as "week 1", then the seven days before that as "week 2", and so forth, try something like this:
SELECT
id,
'w' || TO_CHAR(TRUNC((TRUNC(SYSDATE) - sdate) / 7) + 1) as week,
offer
FROM ... and so on
you also got a format for that.
from OTN
select to_char( date '2008-01-04', 'IW' ) from dual
I think that you could combine the functions TO_CHAR and DECODE, for example:
sql> select decode(to_char(sdate, 'W'),
'1', 'w1',
'2', 'w2',
'3', 'w3',
'4', 'w4',
'5', 'w5')
from table1;
One date minus another date gives their difference in days. Divide by seven to get difference in weeks. Use floor() to get rid of the remainder. Add 1 because you want to start at W1 not W0.
select id
, sdate
, 'W'||trim(to_char(floor((sysdate - sdate)/7) + 1)) as wkno
from your_table
/
Note that wkno comes out as 'W1', 'W2', ... 'W10' which won't sort numerically. There are various workarounds, somne more obvious than opthers, if that's something which botehrs you.
Let's say I have table data similar to the following:
123456 John Doe 1 Green 2001
234567 Jane Doe 1 Yellow 2001
234567 Jane Doe 2 Red 2001
345678 Jim Doe 1 Red 2001
What I am attempting to do is only isolate the records for Jane Doe based upon the fact that she has more than one row in this table. (More that one sequence number)
I cannot isolate based upon ID, names, colors, years, etc...
The number 1 in the sequence tells me that is the first record and I need to be able to display that record, as well as the number 2 record -- The change record.
If the table is called users, and the fields called ID, fname, lname, seq_no, color, date. How would I write the code to select only records that have more than one row in this table? For Example:
I want the query to display this only based upon the existence of the multiple rows:
234567 Jane Doe 1 Yellow 2001
234567 Jane Doe 2 Red 2001
In PL/SQL
First, to find the IDs for records with multiple rows you would use:
SELECT ID FROM table GROUP BY ID HAVING COUNT(*) > 1
So you could get all the records for all those people with
SELECT * FROM table WHERE ID IN (SELECT ID FROM table GROUP BY ID HAVING COUNT(*) > 1)
If you know that the second sequence ID will always be "2" and that the "2" record will never be deleted, you might find something like:
SELECT * FROM table WHERE ID IN (SELECT ID FROM table WHERE SequenceID = 2)
to be faster, but you better be sure the requirements are guaranteed to be met in your database (and you would want a compound index on (SequenceID, ID)).
Try something like the following. It's a single tablescan, as opposed to 2 like the others.
SELECT * FROM (
SELECT t1.*, COUNT(name) OVER (PARTITION BY name) mycount FROM TABLE t1
)
WHERE mycount >1;
INNER JOIN
JOIN:
SELECT u1.ID, u1.fname, u1.lname, u1.seq_no, u1.color, u1.date
FROM users u1 JOIN users u2 ON (u1.ID = u2.ID and u2.seq_no = 2)
WHERE:
SELECT u1.ID, u1.fname, u1.lname, u1.seq_no, u1.color, u1.date
FROM users u1, thetable u2
WHERE
u1.ID = u2.ID AND
u2.seq_no = 2
Check out the HAVING clause for a summary query. You can specify stuff like
HAVING COUNT(*) >= 2
and so forth.