Adding a new column that auto increments based on existing field values - sql

Not sure how to do this, but I want to add a new column "Jersey" that will increment by 1 depending on the "Sport" value in both tab_A and tab_B. So if a "Sport" already exists in tab_B, then just grab the max jersey_no and add 1 for the new column. That one is easy.
Now if the "Sport" does not exist in tab_B, then give it a "Jersey" value of 100 for the new column. However, if there is more than one of the same "Sport" in tab_A (but do not exist in tab_B), then it should start with 100 and increment by 1 for the next same Sport, and so on (e.g. see Garcia example below).
I created a sequence "seqnce" but, that really didn't help at all. Is there another way to accomplish this? Thanks in advance!
Tab_A
Name State Sport
Garcia CA Basketball
Garcia AL Basketball
Garcia NY Basketball
McGee CA Swimming
Tontou CA Football
Tontou AL Swimming
Tab_B
Name Sport Jersey_No
Garcia Swimming 100
Garcia Football 100
McGee Swimming 101
Tontou Swimming 101
Tontou Swimming 102
Expected Output
Name State Sport Jersey
Garcia CA Basketball 100
Garcia AL Basketball 101
Garcia NY Basketball 102
McGee CA Swimming 102
Tontou CA Football 100
Tontou AL Swimming 103
My Code
select name, state, sport
,nvl ((select max(b.jersey_no + 1) from tab_b b
where b.sport = a.sport
and b.name = a.name),
(case
when not exists (select 1 from tab_b b
where b.sport = a.sport
and b.name = a.name
having count(a.sport) > 1)
then seqnce.nextval
else '100'
end )
) Jersey
from tab_a

If it's only select result then using row_number(). If you need update column in table, then write trigger
Example this:
WITH taba AS
(SELECT 'Garcia' Name, 'CA' State, 'Basketball' Sport from dual
UNION ALL
SELECT 'Garcia' Name, 'AL' State, 'Basketball' Sport from dual
UNION ALL
SELECT 'Garcia' Name, 'NY' State, 'Basketball' Sport from dual
UNION ALL
SELECT 'McGee' Name, 'CA' State, 'Swimming' Sport from dual
UNION ALL
SELECT 'Tontou' Name, 'CA' State, 'Football' Sport from dual
UNION ALL
SELECT 'Tontou' Name, 'AL' State, 'Swimming' Sport from dual),
tabb AS
(SELECT 'Garcia' Name, 'Swimming' Sport, 100 Jersey from dual
UNION ALL
SELECT 'Garcia' Name, 'Football', 100 from dual
UNION ALL
SELECT 'McGee' Name, 'Swimming', 101 from dual
UNION ALL
SELECT 'Tontou' Name, 'Swimming', 101 from dual
UNION ALL
SELECT 'Tontou' Name, 'Swimming', 102 from dual)
SELECT taba.Name,
taba.State ,
taba.Sport,
row_number() over(partition by taba.Name, taba.Sport ORDER BY taba.State)
+ nvl((SELECT MAX(tabb.Jersey)
FROM tabb
WHERE taba.name = tabb.name
AND taba.sport = tabb.sport), 99)
FROM taba
result:
Garcia AL Basketball 100
Garcia CA Basketball 101
Garcia NY Basketball 102
McGee CA Swimming 102
Tontou CA Football 100
Tontou AL Swimming 103

Related

Compare Column(data) from different table and view in SQL Developer

I would like to compare few columns in different view with table.
For example:
View a:
student_id First_Name University high_degree
0011221 Tom New York university Y
0011221 Tom MIT N
0011222 Peter Harvard university Y
0011223 Sam Northeastern university Y
0011224 Leo Boston university Y
0011225 Paul Boston college Y (this is not correct)
0011225 Paul Tufts N
table b:
student_id First_Name University degree
0011221 Tom New York university MS
0011222 Peter Harvard university DR
0011223 Sam Northeastern university BS
0011224 Leo Boston university BS
0011225 Paul Tufts DR
Assume that the table b is the most update student information, but in the view a it is the old data from different old table and some student fill the wrong information, then I want to find out which students's information is incorrect.
For example Paul studied two school, Boston college for BS, Tufts for Doctor. In table B is the most update student info, Paul studied Tufts that is the high degree. However in view a Paul's high degree is Boston college so this is wrong information. I am looking for which student has same situation. how to do that?
Ideal result like:
student_id First_Name University high_degree result
0011221 Tom New York university Y match
0011221 Tom MIT N null
0011222 Peter Harvard university Y match
0011223 Sam Northeastern university Y match
0011224 Leo Boston university Y match
0011225 Paul Boston college N null
0011225 Paul Tufts Y not match
No need to same with ideal result, but just need to know which student in view a has different data by comparing with table b.
I appreciate your reply. thank you so much.
One option is to use outer join, while result is retrieved by case expression.
Sample data:
SQL> with
2 view_a (first_name, university, high_degree) as
3 (select 'Tom' , 'New York university', 'Y' from dual union all
4 select 'Tom' , 'MIT' , 'N' from dual union all
5 select 'Peter', 'Harvard university' , 'Y' from dual union all
6 select 'Paul' , 'Boston college' , 'Y' from dual union all
7 select 'Paul' , 'Tufts' , 'N' from dual
8 ),
9 table_b (first_name, university) as
10 (select 'Tom' , 'New York university' from dual union all
11 select 'Peter', 'Harvard university' from dual union all
12 select 'Paul' , 'Tufts' from dual
13 )
Query:
14 select a.first_name, a.university, a.high_degree,
15 case when a.university = b.university then
16 case when a.high_degree = 'Y' then 'Match'
17 when a.high_degree = 'N' then 'Not match'
18 end
19 else null
20 end result
21 from view_a a left join table_b b on a.first_name = b.first_name
22 and a.university = b.university
23 order by a.first_name desc, a.university;
FIRST UNIVERSITY HIGH_DEGREE RESULT
----- ------------------- ------------ ---------
Tom MIT N
Tom New York university Y Match
Peter Harvard university Y Match
Paul Boston college Y
Paul Tufts N Not match
SQL>

How to select column values of a group based on another column value

I got a table like this one
CaseID
NAME
ADDRESS
ZIP
ROLE
1
Joe
address_1
zip_1
role_1
1
John
address_2
zip_2
role_1
1
Jane
address_3
zip_3
role_1
1
Bill
address_4
zip_4
role_1
1
Bill
address_5
zip_5
role_2
2
Bob
address_6
zip_6
role_1
2
Shawn
address_7
zip_7
role_1
I would like to group by the name and CaseID, making a list of the roles in each group. That part is easy. The tricky part is that as you can see for Bill, we have two different addresses and zip. I tried to keep only one with a Max or Min aggregation function inside the group, but there might be inconsistency in the resulting address, keeping zip of one row and the address of the other raw. How can I fetch the zip and address of the same row (which ever) in a group and listing all the roles. I'd like a result like
CaseID
NAME
ADDRESS
ZIP
ROLE
1
Joe
address_1
zip_1
role_1
1
John
address_2
zip_2
role_1
1
Jane
address_3
zip_3
role_1
1
Bill
address_4
zip_4
role_1, role_2
2
Bob
address_6
zip_6
role_1
2
Shawn
address_7
zip_7
role_1
or
CaseID
NAME
ADDRESS
ZIP
ROLE
1
Joe
address_1
zip_1
role_1
1
John
address_2
zip_2
role_1
1
Jane
address_3
zip_3
role_1
1
Bill
address_5
zip_5
role_1, role_2
2
Bob
address_6
zip_6
role_1
2
Shawn
address_7
zip_7
role_1
In Oracle that's a good use case for the keep syntax to aggregate functions:
select caseid, name,
min(address) keep(dense_rank first order by address) address,
min(zip) keep(dense_rank first order by address) zip,
listagg(role, ', ') within group (order by role) role
from mytable
group by caseid, name
The order by clause of keep lets you consistently "pick" a row in the group - that whose address comes first when sorted against others in the group ; we can repeat the expression across columns to get the zip of the same row.
Note that the query would benefit a more stable ordering criteria (such as order by address_id maybe?). Else, from which row should the zip be kept when there are duplicate addresses?
Demo on DB Fiddle
Here's one option; read comments within code.
Sample data:
SQL> with test (caseid, name, address, zip, role) as
2 (select 1, 'Joe' , 'address_1', 'zip_1', 'role_1' from dual union all
3 select 1, 'John', 'address_2', 'zip_2', 'role_1' from dual union all
4 select 1, 'Bill', 'address_4', 'zip_4', 'role_1' from dual union all
5 select 1, 'Bill', 'address_5', 'zip_5', 'role_2' from dual union all
6 select 2, 'Bob' , 'address_6', 'zip_6', 'role_1' from dual
7 ),
Query begins here:
8 temp as
9 -- RN will later be used in correlated subqueries to fetch
10 -- ADDRESS and ZIP values that belong to the same row
11 (select caseid, name, address, zip, role,
12 row_number() over (partition by caseid, name order by address) rn
13 from test
14 )
15 -- finally, fetch what you can from the TEMP CTE; use correlated subqueries
16 -- for the rest and aggregate ROLE values
17 select a.caseid,
18 a.name,
19 --
20 (select b.address
21 from temp b
22 where b.caseid = a.caseid
23 and b.name = a.name
24 and b.rn = 1
25 ) address,
26 --
27 (select b.zip
28 from temp b
29 where b.caseid = a.caseid
30 and b.name = a.name
31 and b.rn = 1
32 ) zip,
33 --
34 listagg(role, ', ') within group (order by role) role
35 from temp a
36 group by a.caseid, a.name
37 /
Result:
CASEID NAME ADDRESS ZIP ROLE
---------- ---- --------- ----- ---------------
1 Joe address_1 zip_1 role_1
1 Bill address_4 zip_4 role_1, role_2
1 John address_2 zip_2 role_1
2 Bob address_6 zip_6 role_1
SQL>
S.t. like
select
caseid, name,
min(address) keep (dense_rank first order by address, zip),
min(zip) keep (dense_rank first order by address, zip),
listagg(role)
from <crappy table>
group by caseid, name

How to search a string inside a string in Oracle

I have a table A with columns Country ID ,Country. and data is like this
US United States
IN India
JP Japan
NP Nepal
etc .
I have different table B which has a column Country which mostly has free text data . Data is like
Texas United States
India KA
XYS Japan WYS
EverestNepal
XYZ
etc.
What i want is that if country column in Table B has a country matching from any column of country in Table A , it should return Country from Table A.
So for the example i gave
Table B has Texas United States --- there is a match in Table A with 'United States' : It should print United States
Table B has India KA ---- there is a match in Table A with 'India':it should print India
EverestNepal --- there is a match in Table A with 'Nepal': it should print Nepal
Table B has
and so on.
To summarize : If the exact match of country in Table B from anywhere in the string is found in Table A, it should print country from Table A
I dont think Like , IN , Substring will work in such situation
INSTR (line #17) is what you probably need.
SQL> with
2 ta (country_id, country) as
3 (select 'US', 'United States' from dual union all
4 select 'IN', 'India' from dual union all
5 select 'JP', 'Japan' from dual union all
6 select 'NP', 'Nepal' from dual
7 ),
8 tb (country) as
9 (select 'Texas United States' from dual union all
10 select 'India KA' from dual union all
11 select 'XYS Japan WYS' from dual union all
12 select 'EverestNepal' from dual union all
13 select 'XYZ' from dual
14 )
15 select b.country B_country,
16 a.country A_country
17 from ta a right join tb b on instr(b.country, a.country) > 0;
B_COUNTRY A_COUNTRY
------------------- -------------
Texas United States United States
India KA India
XYS Japan WYS Japan
EverestNepal Nepal
XYZ
SQL>
You can use like operator to join between the tables:
SELECT
A.COUNTRY
FROM
TABLE_A A
JOIN TABLE_B B ON ( A.COUNTRY LIKE '%'
|| B.COUNTRY
|| '%' );
Cheers!!

How do I get the sum of values from multiple columns in SQL

I have the following table:
Season Name1 Goal1 Name2 Goal2 Name3 Goal3
1990 Smith 2 Abel 1 John 3
1990 Smith 1 Abel 1 John 1
1990 John 0 Smith 2 Abel 5
1991 John 1 Smith 1 Abel 2
1991 Abel 2 John 0 Smith 0
1992 Abel 3 John 0 Smith 1
Season indicates a soccer season, name1, name2 indicates a players position in a given game
Goal1 indicates the number of goals Name1 scored
I would like to generate a list for each name per season how many times they played and the # of goals they scored. Something like this:
Abel 1990 3 games played 7 goals scored
Abel 1991 2 games played 4 goals scored
Abel 1992 1 games played 3 goals scored
John 1990 3 games played 2 goals scored
Any help would be appreciated!
SELECT
sub.player,
sub.Season,
Count(*) AS games_played,
Sum(sub.goals) AS SumOfgoals
FROM
(
SELECT Season, Name1 AS player, Goal1 AS goals
FROM YourTable
UNION ALL
SELECT Season, Name2, Goal2
FROM YourTable
UNION ALL
SELECT Season, Name3, Goal3
FROM YourTable
) AS sub
GROUP BY sub.player, sub.Season
ORDER BY sub.player, sub.Season;
Notice you must use UNION ALL in that subquery. If you use just UNION instead, the subquery result set would include only one row for each combination of Season, player and goals. But when a player scores the same number of goals in more than one game during a season, you want to preserve each of those rows in order to allow an accurate count of games played and total goals scored.
Using your sample data in Access 2007, that query produces this result set.
player Season games_played SumOfgoals
Abel 1990 3 7
Abel 1991 2 4
Abel 1992 1 3
John 1990 3 4
John 1991 2 1
John 1992 1 0
Smith 1990 3 5
Smith 1991 2 1
Smith 1992 1 1
Ok, well, since you aren't sharing what RDBMS you are using, I think that this (ugly) query would work on most of them:
SELECT Name + ' ' + CAST(Season AS VARCHAR(4)) + ' ' +
CAST(Games AS VARCHAR(4)) + ' games played ' +
CAST(Goals AS VARCHAR(4)) + ' goals scored' AS YourColumn
FROM ( SELECT Season, Name, SUM(Goals) AS Goals, COUNT(*) AS Games
FROM ( SELECT Season, Name1 AS Name, Goal1 AS Goals
FROM YourTable
UNION ALL
SELECT Season, Name2 AS Name, Goal2 AS Goals
FROM YourTable
UNION ALL
SELECT Season, Name3 AS Name, Goal3 AS Goals
FROM YourTable) AS A
GROUP BY Season, Name) X
Disclaimer: It is an ugly query.
That's really convoluted, but you could write an in-line query that puts everything into a proper table format so then you can do your usual aggregations and grouping. Keep adding unions for 1 through x, depending on how many columns there are.
SELECT Season, Name, Sum(Goals)
FROM (SELECT Season, Name1 as Name, Goals1 as Goals
FROM table
UNION
SELECT Season, Name2 as Name, Goals2 as Goals
FROM table
UNION
SELECT Season, Name3 as Name, Goals3 as Goals
FROM table) newtable
GROUP BY Season, Name

SQL calculate item frequency using multiple / dependent columns?

I'm completely new to SQL, and have read StackOverflow posts on SQL to try and figure this out, and other sources and unable to do this in SQL. Here goes...
I have a table of 3 columns and thousands of rows, with data for first 2 columns. The third column is currently empty and I need to populate the third column based on data already in the first and second columns.
Say I have states in the first column and fruit entries in the second column. I need to write an SQL statement(s) that calculates the number of different states where each fruit comes from, and then inserts this popularity number into the third column for every row. A popularity number of 1 in that row means that fruit only comes from one state, a popularity number of 4 means the fruit comes from 4 states. So my table is currently like:
state fruit popularity
hawaii apple
hawaii apple
hawaii banana
hawaii kiwi
hawaii kiwi
hawaii mango
florida apple
florida apple
florida apple
florida orange
michigan apple
michigan apple
michigan apricot
michigan orange
michigan pear
michigan pear
michigan pear
texas apple
texas banana
texas banana
texas banana
texas grape
And I need to figure out how to calculate and then update the third column, named popularity, which is the number of states that exports that fruit. The goal is to produce (sorry bad pun) the table below, where based on above table, "apple" appears in all 4 states, oranges and banana appear in 2 states, and kiwi, mango, pear, and grape only appear in 1 state, hence their corresponding popularity numbers.
state fruit popularity
hawaii apple 4
hawaii apple 4
hawaii banana 2
hawaii kiwi 1
hawaii kiwi 1
hawaii mango 1
florida apple 4
florida apple 4
florida apple 4
florida orange 2
michigan apple 4
michigan apple 4
michigan apricot 1
michigan orange 2
michigan pear 1
michigan pear 1
michigan pear 1
texas apple 4
texas banana 2
texas banana 2
texas banana 2
texas grape 1
My small programmer brain says to try and figure out a way to loop through the data in some kind of script, but reading up a little on SQL and databases, it seems like you don't write long and slow looping scripts in SQL. I'm not even sure if you can? but instead that there are better/faster ways to do this in SQL.
Anyone know how to, in SQL statement(s), calculate and update the third column for each row, which is here called popularity and corresponds to the number of states that each fruit comes from? Thanks for reading, very grateful for any help.
So far I have tried these SQL statements below, which output but don't quite get me what I need:
--outputs those fruits appearing multiple times in the table
SELECT fruit, COUNT(*)
FROM table
GROUP BY fruit
HAVING COUNT(*) > 1
ORDER BY COUNT(*) DESC
--outputs those fruits appearing only once in the table
SELECT fruit, COUNT(*)
FROM table
GROUP BY fruit
HAVING COUNT(*) = 1
--outputs list of unique fruits in the table
SELECT COUNT (DISTINCT(fruit))
FROM table
If you want to simply update your table with the priority it would look like:
update my_table x
set popularity = ( select count(distinct state)
from my_table
where fruit = x.fruit )
If you want to select the data then you can use an analytic query:
select state, fruit
, count(distinct state) over ( partition by fruit ) as popularity
from my_table
This provides the number of distinct states, per fruit.
I ran this and got (what I think) is what you want:
WITH t
AS (SELECT 'hawaii' as STATE, 'apple' as fruit FROM dual
UNION ALL
SELECT 'hawaii' as STATE, 'apple' as fruit FROM dual
UNION ALL
SELECT 'hawaii' as STATE, 'banana' as fruit FROM dual
UNION ALL
SELECT 'hawaii' as STATE, 'kiwi' as fruit FROM dual
UNION ALL
SELECT 'hawaii' as STATE, 'kiwi' as fruit FROM dual
UNION ALL
SELECT 'hawaii' as STATE, 'mango' as fruit FROM dual
UNION ALL
SELECT 'florida' as STATE, 'apple' as fruit FROM dual
UNION ALL
SELECT 'florida' as STATE, 'apple' as fruit FROM dual
UNION ALL
SELECT 'florida' as STATE, 'apple' as fruit FROM dual
UNION ALL
SELECT 'florida' as STATE, 'orange' as fruit FROM dual
UNION ALL
SELECT 'michigan' as STATE, 'apple' as fruit FROM dual
UNION ALL
SELECT 'michigan' as STATE, 'apple' as fruit FROM dual
UNION ALL
SELECT 'michigan' as STATE, 'apricot' as fruit FROM dual
UNION ALL
SELECT 'michigan' as STATE, 'orange' as fruit FROM dual
UNION ALL
SELECT 'michigan' as STATE, 'pear' as fruit FROM dual
UNION ALL
SELECT 'michigan' as STATE, 'pear' as fruit FROM dual
UNION ALL
SELECT 'michigan' as STATE, 'pear' as fruit FROM dual
UNION ALL
SELECT 'texas' as STATE, 'apple' as fruit FROM dual
UNION ALL
SELECT 'texas' as STATE, 'banana' as fruit FROM dual
UNION ALL
SELECT 'texas' as STATE, 'banana' as fruit FROM dual
UNION ALL
SELECT 'texas' as STATE, 'banana' as fruit FROM dual
UNION ALL
SELECT 'texas' as STATE, 'grape' as fruit FROM dual)
SELECT state,
fruit,
count(DISTINCT state) OVER (PARTITION BY fruit) AS popularity
FROM t;
Returned
florida apple 4
florida apple 4
florida apple 4
hawaii apple 4
hawaii apple 4
michigan apple 4
michigan apple 4
texas apple 4
michigan apricot 1
hawaii banana 2
texas banana 2
texas banana 2
texas banana 2
texas grape 1
hawaii kiwi 1
hawaii kiwi 1
hawaii mango 1
florida orange 2
michigan orange 2
michigan pear 1
michigan pear 1
Obviously, you'd only need to run:
SELECT state,
fruit,
count(DISTINCT state) OVER (PARTITION BY fruit) AS popularity
FROM table_name;
Hope it helps...
If your table is #fruit...
To count the different states for each fruit
select fruit, COUNT(distinct state) statecount from #fruit group by fruit
and so to update the table with these values
update #fruit
set popularity
= statecount
from
#fruit
inner join
(select fruit, COUNT(distinct state) statecount from #fruit group by fruit) sc
on #fruit.fruit = sc.fruit
This should get you most of the way there. Basically you want to get a count of distinct states that the fruit is in and then use that to join back to the original table.
update table
set count = cnt
from
(
select fruit, count(distinct state) as cnt
from table
group by fruit) cnts
inner join table t
on cnts.fruit = t.fruit
Another option:
SELECT fruit
, COUNT(*)
FROM
(
SELECT state
, fruit
, ROW_NUMBER() OVER (PARTITION BY state, fruit ORDER BY NULL) rn
FROM t
)
WHERE rn = 1
GROUP BY fruit
ORDER BY fruit;
Try this:
select a.*,b.total
from [table] as a
left join
(
SELECT fruit,count(distinct [state]) as total
FROM [table]
group by fruit
) as b
on a.fruit = b.fruit
Note this is SQL Server code, do your own tweaks if necessary.
try this
create table states([state] varchar(10),fruit varchar(10),popularity int)
INSERT INTO states([state],fruit)
VALUES('hawaii','apple'),
('hawaii','apple'),
('hawaii','banana'),
('hawaii','kiwi'),
('hawaii','kiwi'),
('hawaii','mango'),
('florida','apple'),
('florida','apple'),
('florida','apple'),
('florida','orange'),
('michigan','apple'),
('michigan','apple'),
('michigan','apricot'),
('michigan','orange'),
('michigan','pear'),
('michigan','pear'),
('michigan','pear'),
('texas','apple'),
('texas','banana'),
('texas','banana'),
('texas','banana'),
('texas','grape')
update t set t.popularity=a.cnt
from states t inner join
(SELECT fruit,count(distinct [state]) as cnt
FROM states
group by fruit) a
on t.fruit =a.fruit