Oracle SQL Developer - Concat Data - sql

Good Day,
I have a table which has a column which contains numbers only. However due its containing rule data for special IDs it giving back the result in more lines, for example:
you have a bookstore DB where all the costumers have an ID and the books also have their own ID. When you wish to know that how many books have been ordered by each costumer where the result is bugger than 1 it will appear in multiple lines, where the ID of the costumer is the same but in every line the books' IDs are different.
My question is:
What is the proper syntax, or code portion to use to get one line only for each and every costumer, where the book IDs are separated by commas in the same column?
I've tried like this:
BOOK_CONT AS (
SELECT DISTINCT BOOK_ID, LISTAGG(' ('||BOOK_ID||')', '; ')WITHIN GROUP (ORDER BY BOOK_ID) AS BOOKS
FROM BOOK_LIST)

You would use group_by and listagg(). Assuming that your table has columns customer_id and book_id, you would go:
select
customer_id,
listagg(book_id, ', ') within group(order by book_id) all_book_ids
from book_list
group by customer_id

Related

CONCAT values in specific cells based off values in a different column

I need help and apologies in advance for not including a code because I do not know where to even start.
I work as a reporting analyst and have a table full of customer data + customer remarks. I need to contact the customer remarks and cannot think of a way to do so.
My table has 7 columns and I need to concatenate the REMARK_TEXTS rows before sending the report off to our internal clients.
The remarks are based on the REMARKS_SEQ_NBR column and the sequence numbers aligned with the REMARK TEXTS. I am including a screenshot of an excel mockup I created.
This is my query using LISTAGG():
SELECT DISTINCT
AA.ID_NUMBER,
AA.LAST_NAME,
AA.FIRST_NM,
AA.DATE_OF_BIRTH,
AA.CUSTOMER_SINCE,
AA.REMARKS_SEQ_NBR
LISTAGG(‘REMARK_TEXTS’
) WITHIN GROUP (
AA.ID_NUMBER
)
AS CUSTOMERS
FROM CUSTOMER_CURRENT
GROUP BY ID_NUMBER
ORDER BY ID_NUMBER
Thank you in advance!
enter image description here
You're trying to combine multiple rows into one, so you need an aggregate function. For concatenating text, that's LISTAGG. You have it pretty much right:
select id_number, last_name, first_name, date_of_birth, customer_since
listagg( trim( remark_texts ), ' ' ) within group ( order by remarks_seq_nbr )
as customers
from customer_current
group by id_number, last_name, first_name, date_of_birth, customer_since
order by id_number
I'm trimming the text and using space as a separator, so you don't wind up with CUSTOMER PREFERS DONOT CALL.

Return all data when grouping on a field

I have the following 2 tables (there are more fields in the real tables):
create table publisher(id serial not null primary key,
name text not null);
create table product(id serial not null primary key,
name text not null,
publisherRef int not null references publisher(id));
Sample data:
insert into publisher (id,name) values (1,'pub1'),(2,'pub2'),(3,'pub3');
insert into product (name,publisherRef) values('p1',1),('p2',2),('p3',2),('p4',2),('p5',3),('p6',3);
And I would like the query to return:
name, numProducts
pub2, 3
pub3, 2
pub1, 1
A product is published by a publisher. Now I need a list of name, id of all publishers which have at least one product, ordered by the total number of products each publisher has.
I can get the id of the publishers ordered by number of products with:
select publisherRef AS id, count(*)
from product
order by count(*) desc;
But I also need the name of each publisher in the result. I thought I could use a subquery like:
select *
from publisher
where id in (
select publisherRef
from product
order by count(*) desc)
But the order of rows in the subquery is lost in the outer SELECT.
Is there any way to do this with a single sql query?
SELECT pub.name, pro.num_products
FROM (
SELECT publisherref AS id, count(*) AS num_products
FROM product
GROUP BY 1
) pro
JOIN publisher pub USING (id)
ORDER BY 2 DESC;
db<>fiddle here
Or (since the title mentions "all data") return all columns of the publisher with pub.*. After products have been aggregated in the subquery, you are free to list anything in the outer SELECT.
This only lists publisher which
have at least one product
And the result is ordered by
the total number of products each publisher has
It's typically faster to aggregate the "n"-table before joining to the "1"-table. Then use an [INNER] JOIN (not a LEFT JOIN) to exclude publishers without products.
Note that the order of rows in an IN expression (or items in the given list - there are two syntax variants) is insignificant.
The column alias in publisherref AS id is totally optional to use the simpler USING clause for identical column names in the following join condition.
Aside: avoid CaMeL-case names in Postgres. Use unquoted, legal, lowercase names exclusively to make your life easier.
Are PostgreSQL column names case-sensitive?

SQL query to identify patterns in Parent-Child relationship

I am working with an Oracle database in a financial institution. It has a Credit Facility table and a Loan table, in a parent-child relationship (1-many).
As part of a project, they added a new field called Type Code to both of these tables (using some complex logic involving the values of a bunch of other fields). I assumed that in the vast majority of cases the parent Credit Facility and all of the children Loans would be assigned the same Type Code. But it turns out that there are hundreds of thousands of cases where the Credit Facility and the Loan have different Type Codes, and all those cases have to be "handled" somehow. I was able to make a query to generate a list of all the Credit Facilities and related Loans where 1 or more Loans have a different Type Code from its parent, and the result was 600K records.
Results look like this (Simplified)
Now I want to break it down into patterns, hopefully using 1 field that I can group by. The field should have values like below:
The Pattern field should always generate the same value based on the parent's Type Code and the unique values of the children. I don't care how many child Loans there are of each type or what order they come in.
Any ideas for how to generate this PATTERN field in a SQL query? I could also do it in Excel in a pinch, but not even sure how to do that, short of writing VBA code, which is my last resort.
Thank you!
The following will produce the results you want, assuming you rename your TYPE fields so they're distinct from one another - one should probably be FACILITY_TYPE and the other should apparently be LOAN_TYPE:
WITH cteDistinct_facility_loan_types
AS (SELECT DISTINCT FACILITY_NUM,
LOAN_TYPE
FROM YOUR_TABLE),
cteFacility_loan_types
AS (SELECT FACILITY_NUM,
LISTAGG(LOAN_TYPE, ',')
WITHIN GROUP (ORDER BY FACILITY_NUM) AS FACILITY_LOAN_TYPES
FROM cteDistinct_facility_loan_types
GROUP BY FACILITY_NUM)
SELECT t.*,
t.FACILITY_TYPE || '-' || flt.FACILITY_LOAN_TYPES AS PATTERN
FROM YOUR_TABLE t
INNER JOIN cteFacility_loan_types flt
ON flt.FACILITY_NUM = t.FACILITY_NUM
ORDER BY t.FACILITY_NUM
db<>fiddle here
If you wanted to look at the patterns for the facilities, then you can use listagg() as an analytic function:
select t.*,
(t.facility_num || '-' ||
listagg(t.loan_type, ',') within group (order by loan_num) over (partition by facility_num)
) as pattern
from t;
These results are a bit different from what you have. They start with the facility type and they include all loans, including duplicates.
If you want the distinct loan_types, then use a subquery:
select t.*,
(t.facility_num || '-' ||
listagg(case when seqnum = 1 then t.loan_type end, ',') within group
(order by loan_num) over
(partition by facility_num)
) as pattern
from (select t.*,
row_number() over (partition by facility_num, loan_type order by loan_num) as seqnum
from t
) t
Here is a db<>fiddle.

SQL: Find duplicates and for each duplicate group assign value of first duplicate of that group

I have the results in the top table. I would like the results in the bottom table.
Using an SQL query on the table above, I would like to find groups of duplicates (where the values in all columns except Id and Category are identical) and from that create a result that has for each entry the lowest Id from its group of duplicates and the (unmodified) Category from the original table.
Window function min can be used here:
select min(id) over (partition by first_name, last_name, company) id,
category
from t;

How to insert a count column into a sql query

I need the second column of the table retrieved from a query to have a count of the number of rows, so row one would have a 1, row 2 would have a 2 and so on. I am not very proficient with sql so I am sorry if this is a simple task.
A basic example of what I am doing would be is:
SELECT [Name], [I_NEED_ROW_COUNT_HERE],[Age],[Gender]
FROM [customer]
The row count must be the second column and will act as an ID for each row. It must be the second row as the text file it is generating will be sent to the state and they require a specific format.
Thanks for any help.
With your edit, I see that you want a row ID (normally called row number rather than "count") which is best gathered from a unique ID in the database (person_id or some other unique field). If that isn't possible, you can make one for this report with ROW_NUMBER() OVER (ORDER BY EMPLOYEE_ID DESC) AS ID, in your select statement.
select Name, ROW_NUMBER() OVER (ORDER BY Name DESC) AS ID,
Age, Gender
from customer
This function adds a field to the output called ID (see my tips at the bottom to describe aliases). Since this isn't in the database, it needs a method to determine how it will increment. After the over keyword it orders by Name in descending order.
Information on Counting follows (won't be unique by row):
If each customer has multiple entries but the selected fields are the same for that user and you are counting that user's records (summed in one result record for the user) then you would write:
select Name, count(*), Age, Gender
from customer
group by name, age, gender
This will count (see MSDN) all the user's records as grouped by the name, age and gender (if they match, it's a single record).
However, if you are counting all records so that your whole report has the grand total on every line, then you want:
select Name, (select count(*) from customer) as "count", Age, Gender
from customer
TIP: If you're using something like SSMS to write a query, dragging in columns will put brackets around the columns. This is only necessary if you have spaces in column names, but a DBA will tend to avoid that like the plague. Also, if you need a column header to be something specific, you can use the as keyword like in my first example.
W3Schools has a good tutorial on count()
The COUNT(column_name) function returns
the number of values (NULL values will not be counted) of the
specified column:
SELECT COUNT(column_name) FROM table_name;
The COUNT(*) function returns the number of records in a table:
SELECT COUNT(*) FROM table_name;
The COUNT(DISTINCT column_name) function returns the number of
distinct values of the specified column:
SELECT COUNT(DISTINCT column_name) FROM table_name;
COUNT(DISTINCT) works with ORACLE and Microsoft SQL Server, but
not with Microsoft Access.
It's odd to repeat the same number in every row but it sounds like this is what you're asking for. And note that this might not work in your flavor of SQL. MS Access?
SELECT [Name], (select count(*) from [customer]), [Age], [Gender]
FROM [customer]