SQL query dynamic row generation with composite key - sql

My question is made of 3 parts.
First part:
Is there a way to generate rows based on a value?
E.g:
I want to give each family a number of vouchers based on their family_members_count.
Each voucher should have a unique id:
Base table:
id name family_members_count
1 fadi 2
2 sami 3
3 ali 1
Result:
family_id name voucher_id
1 fadi 121
1 fadi 122
2 sami 123
2 sami 124
2 sami 125
3 ali 126
Second part:
Can I control the voucher_id composite key? I want the voucher_id to be like this
(location)(cycle)(sequence 5 digits)
If north = 08 and we are in the second cycle it should be:
080200001
080200002
... and so on.
Third part:
I need the solution in both MS Access 2010 SQL and PostgreSQL 9.1 SQL.

Question 1
Use generate_series(). (In the coming version 9.3 look for the key word LATERAL.)
SELECT id AS family_id
,name
,120 + generate_series(1, family_members_count) AS voucher_id
FROM fam;
-> sqlfiddle demo
Question 2
SELECT id AS family_id
,name
,location
|| to_char(cycle, 'FM00')
|| to_char(generate_series(1, family_members_count), 'FM00000')
AS voucher_id
FROM fam2;
-> sqlfiddle demo
Note the use of to_char() to format numbers as text - and in particular the use of the FM pattern modifier to avoid leading white space.
Question 3
Sorry, I got MS Access out of my system 10 years ago and never looked back. Somebody else might fill in. I doubt it will be as simple.

Related

Case statement logic and substring

Say I have the following data:
Passes
ID | Pass_code
-----------------
100 | 2xBronze
101 | 1xGold
102 | 1xSilver
103 | 2xSteel
Passengers
ID | Passengers
-----------------
100 | 2
101 | 5
102 | 1
103 | 3
I want to count then create a ticket in the output of:
ID 100 | 2 pass (bronze)
ID 101 | 5 pass (because it is gold, we count all passengers)
ID 102 | 1 pass (silver)
ID 103 | 2 pass (steel)
I was thinking something like the code below however, I am unsure how to finish my case statement. I want to substring pass_code so that we get show pass numbers e.g '2xBronze' should give me 2. Then for ID 103, we have 2 passes and 3 customers so we should output 2.
Also, is there a way to firstly find '2xbronze' if the pass_code contained lots of other things such as '101001, 1xbronze, FirstClass' - this may change so i don't want to substring, could we search for '2xbronze' and then pull out the 2??
SELECT
CASE
WHEN Passes.pass_code like '%gold%' THEN Passengers.passengers
WHEN Passes.pass_code like '%steel%' THEN SUBSTRING(passes.pass_code, 1,1)
WHEN Passes.pass_code like '%bronze%' THEN SUBSTRING(passes.pass_code, 1,1)
WHEN Passes.pass_code like '%silver%' THEN SUBSTRING(passes.pass_code, 1,1)
else 0 end as no,
Passes.ID,
Passes.Pass_code,
Passengers.Passengers
FROM Passes
JOIN Passengers ON Passes.ID = Passengers.ID
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=db698e8562546ae7658270e0ec26ca54
So assuming you are indeed using Oracle (as your DB fiddle implies).
You can do some string magic with finding position of a splitter character (in your case the x), then substringing based on that. Obviously this has it's problems, and x is a bad character seperator as well.. but based on your current set.
WITH PASSCODESPLIT AS
(
SELECT PASSES.ID,
TO_Number(SUBSTR(PASSES.PASS_CODE, 0, (INSTR(PASSES.PASS_CODE, 'x')) - 1)) AS NrOfPasses,
SUBSTR(PASSES.PASS_CODE, (INSTR(PASSES.PASS_CODE, 'x')) + 1) AS PassType
FROM Passes
)
SELECT
PASSCODESPLIT.ID,
CASE
WHEN PASSCODESPLIT.PassType = 'gold' THEN Passengers.Passengers
ELSE PASSCODESPLIT.NrOfPasses
END AS NrOfPasses,
PASSCODESPLIT.PassType,
Passengers.Passengers
FROM PASSCODESPLIT
INNER JOIN Passengers ON PASSCODESPLIT.ID = Passengers.ID
ORDER BY PASSCODESPLIT.ID ASC
Gives the result of:
ID NROFPASSES PASSTYPE PASSENGERS
100 2 bronze 2
101 5 gold 5
102 1 silver 1
103 2 steel 3
As can also be seen in this fiddle
But I would strongly advise you to fix your table design. Having multiple attributes in the same column leads to troubles like these. And the more variables/variations you start storing, the more 'magic' you need to keep doing.
In this particular example i see no reason why you don't simply have the 3 columns in Passes, also giving you the opportunity to add new columns going forward. I.e. to keep track of First class.
You can extract the numbers using regexp_substr(). So I think this does what you want:
SELECT (CASE WHEN p.pass_code LIKE '%gold%'
THEN TO_NUMBER(REGEXP_SUBSTR(p.pass_code, '^[0-9]+'))
ELSE pp.passengers
END) as num,
p.ID, p.Pass_code, pp.Passengers
FROM Passes p JOIN
Passengers pp
ON p.ID = pp.ID;
Here is a db<>fiddle.
This converts the leading digits in the code to a number. Also note the use of table aliases to simplify the query.

Extracting data from column to a new column sql developer

I've imported data into SQL developer and I need to separate data from one column into a new column. For example I have:
Temp_Title
Congo (1995)
Nadja (1993)
I need to remove the year from the title into a new column named temp_year. I was told that I can use "Parse" but I'm not sure where to start. Any help is greatly appreciated.
You didn't specify database you use; also, you mentioned "SQL Developer" (designed by Oracle) but tagged the question with "plsqldeveloper" tag so - actual query might depend on certain info you didn't share with us.
Anyway, to get you started, here's an example (based on Oracle) which uses two options:
the first one uses traditional SUBSTR + INSTR combination
another one uses regular expressions
.
SQL> with test (temp_title) as
2 (select 'Congo (1995)' from dual union all
3 select 'Nadja (1993)' from dual union all
4 select 'Back to the future (1985)' from dual
5 )
6 select
7 substr(temp_title, 1, instr(temp_title, '(') - 1) title,
8 substr(temp_title, instr(temp_title, '(') + 1, 4) year,
9 --
10 regexp_substr(temp_title, '(\w| )+') title2,
11 rtrim(regexp_substr(temp_title, '\d+\)$'), ')') year2
12 from test;
TITLE YEAR TITLE2 YEAR2
-------------------- ---------------- -------------------- -----
Congo 1995 Congo 1995
Nadja 1993 Nadja 1993
Back to the future 1985 Back to the future 1985
SQL>

SQL composite key value vs string

I have a list of integer from 1 to N elements (N < 24)
At the moment, there are two solutions to manage this value in a SQL database (I think it is the same for MySQL and Microsoft SQL Server)
Solution 1: use VARCHAR and , to separate integer values:
aaa | 40,50,50,10,600,200
aab | 40,50,600,200
aac | 40,50,50,10,600,200,500,1
Solution 2: create a new table with composite primary key (key, id) (id = index of element in list) and value:
aaa | 0 | 40
aaa | 1 | 50
aaa | 2 | 50
....
aab | 0 | 40
aab | 1 | 50
aab | 2 | 600
....
What is it better solution considering I have many items of data to load and I need to refresh this data many times
Thanks
Edit:
my operative case is: i need to refresh/read all data (list for key) with same call and i never call one by one, this is why i think first approach better.
And all math like avg or max i wanna do on client.
Usually the second approach is preferable. One advantage is ease of access:
-- Third value of aaa
select value from mytable where key = 'aaa' and pos = 3;
-- Avarage value of aaa
select avg(value) from mytable where key = 'aaa';
-- Avarage number of values
select avg(cnt) from (select count(*) as cnt from mytable group by key) counted;
Another is data consistency. You can add simple constraints to your columns, such as to allow only integers from, say, 1 to 700 and positions only up to 23.
There is an exception to the above, though. If you use the database only to store the list as is and you don't want to select separate values or even aggregate them, i.e. if this is just a string to the DBMS and your queries don't care about its content, then store it as a simple string. Why not?
The second solution that you propose is the classic way of doing this, I would recommend that.
The first solution is quite terrible in scaling and in other hundred things

Populating column for Oracle Text search from 2 tables

I am investigating the benefits of Oracle Text search, and currently am looking at collecting search text data from multiple (related) tables and storing the data in the smaller table in a 1-to-many relationship.
Consider these 2 simple tables, house and inhabitants, and there are NEVER any uninhabited houses:
HOUSE
ID Address Search_Text
1 44 Some Road
2 31 Letsby Avenue
3 18 Moon Crescent
INHABITANT
ID House Name Nickname
1 1 Jane Doe Janey
2 1 John Doe JD
3 2 Jo Smythe Smithy
4 2 Percy Plum PC
5 3 Apollo Lander Moony
I want to to write SQL that updates the HOUSE.Search_Text column with text from INHABITANT. Now because this is a 1-to-many, the SQL needs to collate the data in INHABITANT for each matching row in house, and then combine the data (comma separated) and update the Search_Text field.
Once done, the Oracle Text search index on HOUSE.Search_Text will return me HOUSEs that match the search criteria, and I can look up INHABITANTs accordingly.
Of course, this is a very simplified example, I want to pick up data from many columns and Full Text Search across fields in both tables.
With the help of a colleague we've got:
select id, ADDRESS||'; '||Names||'; '||Nicknames as Search_Text
from house left join(
SELECT distinct house_id,
LISTAGG(NAME, ', ') WITHIN GROUP (ORDER BY NAME) OVER (PARTITION BY house_id) as Names,
LISTAGG(NICKNAME, ', ') WITHIN GROUP (ORDER BY NICKNAME) OVER (PARTITION BY house_id) as Nicknames
FROM INHABITANT)
i on house.id = i.house_id;
which returns:
1 44 Some Road; Jane Doe, John Doe; JD, Janey
2 31 Letsby Avenue; Jo Smythe, Percy Plum; PC, Smithy
3 18 Moon Crescent; Apollo Lander; Moony
Some questions:
Is this an efficient query to return this data? I'm slightly
concerned about the distinct.
Is this the right way to use Oracle Text search across multiple text fields?
How to update House.Search_Text with the results above? I think I need a correlated subquery, but can't quite work it out.
Would it be more efficient to create a new table containing House_ID and Search_Text only, rather than update House?

Table structure of a student

I want a table structure which can store the details of the student like the below format.
If the student is in
10 th standard -> I need his aggregate % from 1st standard to 9th standard.
5 th standard -> I need his aggregate % from 1st standard to 4th standard.
1 st standard -> No aggregate % has to be displayed.
And the most important thing is ' we need to use only one table'. Please form a table structure with no redundant values.
Any ideas will be greatly appreciated......
No friends this is not a home work. This is asked in Oracle interview, conducted in Hyderabad day before yesterday '24th July, 2010',. He asked me the table structure.
He even did not asked me the query. He asked me how I will design the table. Please advice me.
id | name | grade | aggregate
This would do the trick, id is your primary key, name is students first last name, grade is what grade he is in and aggregate is aggregate % based on the grade.
Fro example some rows might be:
10 | Bill Cosby | 10 | 90
11 | Jerry Seinfeld | 4 | 60
Bill Cosby would have aggregate percent of 90 in grades 1-9, and jerry would have 60 in grades 1-3. In this case it is one table and boils down to you managing the rule of aggregation for this table, since it has to be one table.
If this is an interview question, it looks like they would like to check your knowledge on Nested Tables. Essentially you would have one column as roll number, and other column which is a nested table as Class and Percentage.