I need an oracle sql extract - sql

So I am newer to oracle sql and need a data extract.
I have a table with informations about customers (customers), they can have multiple emails (emailaddresses) which can have multiple usages (usage).
At the moment I have something like this:
Select emailaddresses.email as primary, customer.uid as customerUid,
emailaddresses.email as workmail
Join emailaddresses on emailaddresses.parentid = customer.id
Join usages on usages.parent_id = emailaddresses.id .... -- here I am stuck
workmail: (where usage.usagetype = 'work';)
and primary: (where usage.usagetype = 'primary';)
-- now the issue is, i dont know how to select both workmails and primary mails into this extract for one and the same customer. (and customer - uid and id are not the same, I did not invent it and I cannot change it. I just need an extract)
my tables and columns:
customer
uid (int)
id (varchar)
usages
parent_id (int) -- links to emailaddresses.id
customer_id (varchar) -- links to customer.id
usagetype (varchar)
emailaddresses
id (int)
parentid (varchar) -- links to customer.id
email (varchar)
My expected outcome:
customeruid
primary
workmail
01234
example#mail.com
example#workmail.com
01235
mail#mail.com
example#work.com
01236
mail1#mail2.com
mail#work2.com

one way you could do is use LISTAGG as follows,
select customer_id, listagg(email_id || ' , ') within group (order by customer_id) FROM (
select 1 as customer_id, 'hk#gmail.com' as email_id , 'primary' as usagetype from dual UNION
select 1 as customer_id, 'hk#tmail.com' as email_id , 'work' as usagetype from dual UNION
select 2 as customer_id, 'tt#tmail.com' as email_id , 'work' as usagetype from dual
)group by customer_id

This should do:
SELECT *
FROM (SELECT CST.UID
, EA.EMAIL,
, USG.USAGETYPE
FROM EMAILADDRESSES EA
, CUSTOMER CST
, USAGES USG
WHERE EA.PARENTID = CST.ID
AND EA.ID = USG.PARENT_ID
AND USG.USAGETYPE IN ('work','primary'))
PIVOT (MAX(EMAIL) FOR USAGETYPE IN ('work','primary'));

Related

SQL: rows that share several values on a specific column

I have a table Visited with 2 columns:
ID | City
ID is an integer, City is a string.
Note that none of the columns is a key by itself - we can have the same ID visiting several cities, and several different IDs in the same city.
Given a specific ID, I want to return all the IDs in the table that visited at least half of the places that the input ID did (not including themselves)
edit: We only count places that are the same.
so if
ID 1 visited cities a,b,c.
ID 2 visited b,c,d.
ID 3 visited c,d,e.
then for ID=1 we return only [2], because out of the three cities ID1 visited, ID3 visited only one
Inner join the visited table with the list of cities visited by the specific id, then select ids with at least half of the number of rows when grouped by id.
with u as
(select city as visitedBySpecificId from visited where id = *specificId*),
v as
(select * from visited inner join u on city = visitedBySpecificId where id <> *specificId*)
(select id from v group by id having count(*) >= (select count(*) from u)/2.0)
Fiddle
Join them and compare the counts.
create table suspect_tracking (id int, city varchar(30))
insert into suspect_tracking values
(1, 'Brussels'), (1,'London'), (1,'Paris')
, (1,'New York'), (1,'Bangkok'), (1, 'Hong Kong')
, (1,'Dubai'), (1,'Singapoor'), (1,'Rome')
, (1,'Macau'), (1, 'Istanbul'), (1,'Kuala Lumpur')
, (1,'Dehli'), (1,'Tokyo'), (1,'Moscow')
, (2,'New York'), (2,'Bangkok'), (2, 'Hong Kong')
, (2,'Dubai'), (2,'Singapoor'), (2,'Rome')
, (2,'Macau'), (2, 'Istanbul'), (2,'Kuala Lumpur')
, (3,'Macau'), (3, 'Istanbul'), (3,'Kuala Lumpur')
, (3,'Dehli'), (3,'Tokyo'), (3,'Moscow')
with cte_suspects as (
select id, city
from suspect_tracking
group by id, city
)
, cte_prime_suspect as (
select distinct id, city
from suspect_tracking
where id = 1
)
, cte_prime_total as (
select id, count(city) as cities
from cte_prime_suspect
group by id
)
select sus.id
from cte_prime_suspect prime
join cte_prime_total primetot
on primetot.id = prime.id
join cte_suspects sus
on sus.city = prime.city and sus.id <> prime.id
group by prime.id, sus.id, primetot.cities
having count(sus.city) >= primetot.cities/2
| id |
| -: |
| 2 |
db<>fiddle here

SQL query with GROUP BY and HAVING

I have a little problem in mi project, i'm trying to make a query on a single table but I'm not succeeding.
The table is this:
CREATE TABLE PARTITA(
COD_SFIDA VARCHAR (20) PRIMARY KEY,
DATA_P DATE NOT NULL,
RISULTATO CHAR (3) NOT NULL,
COD_DECK_IC VARCHAR (15),
COD_DECK_FC VARCHAR (15),
COD_EVT VARCHAR (15),
TAG_USR_IC VARCHAR (15),
TAG_USR_FC VARCHAR (15),
CONSTRAINT CHECK_RISULTATO CHECK (RISULTATO='0-0' OR RISULTATO='0-1' OR RISULTATO='1-0' OR RISULTATO='1-1'),
CONSTRAINT FK8 FOREIGN KEY (COD_DECK_IC, TAG_USR_IC) REFERENCES DECK (COD_DECK, TAG_USR) ON DELETE CASCADE,
CONSTRAINT FK17 FOREIGN KEY (COD_DECK_FC, TAG_USR_FC) REFERENCES DECK (COD_DECK, TAG_USR) ON DELETE CASCADE,
CONSTRAINT FK9 FOREIGN KEY (COD_EVT) REFERENCES TORNEO (COD_EVENTO) ON DELETE CASCADE
);
I would like to view the most used deck by each user.
this is the query I tried to do:
SELECT P.COD_DECK_FC, P.TAG_USR_FC, COUNT(P.COD_DECK_FC)
FROM PARTITA P
GROUP BY P.TAG_USR_FC, P.COD_DECK_FC
UNION
SELECT P.COD_DECK_IC, P.TAG_USR_IC, COUNT(P.COD_DECK_IC)
FROM PARTITA P
GROUP BY P.TAG_USR_IC, P.COD_DECK_IC
/
But I would like to view just the most used deck by each user and don't all the decks and how many times users used them.
How can I do?
I would like the query to show the tag_usr and the cod_deck that is used the most for all of this for each user
eg:
cod_deck tag_usr count(cod_deck)
------------- ----------- --------------
1 A1BE2 5
2 AE3NF 6
5 FNKJD 3
instead the previious query returns to me:
cod_deck tag_usr count(cod_deck)
------------- ----------- --------------
1 A1BE2 5
2 AE3NF 6
5 FNKJD 3
2 A1BE2 2
1 AE3NF 3
I just want that the query show me the users A1BE2 and AE3NF just one time, because the query have to select the most used deck of each user.
You don't want to select a field that you're counting. Try something like this:
SELECT P.COD_DECK_FC, P.TAG_USR_FC, COUNT(P.COD_SFIDA)
FROM PARTITA P
GROUP BY P.COD_DECK_FC, P.TAG_USR_FC
UNION
SELECT P.COD_DECK_IC, P.TAG_USR_IC, COUNT(P.COD_SFIDA)
FROM PARTITA P
GROUP BY P.COD_DECK_IC, P.TAG_USR_IC
That will list all of the combinations of COD_DECK_FC and TAG_USR_FC
and then number of times it appears in the table, and then do the same with COD_DECK_IC and TAG_USR_IC. It's not clear to me from your question exactly what you want, but I know that you shouldn't put a field in COUNT if you're selecting it.
If i understand correctly you need subquery with ranking function :
with t as (
select *, row_number() over (partition by cod_deck order by count desc) Seq
from (<union query>)
)
select *
from cte c
where seq = 1;
I think you want this:
with ct as (
select P.COD_DECK_FC as deck, P.TAG_USR_FC as usr, COUNT(P.COD_DECK_FC) as cnt
from partita p
group by P.TAG_USR_FC, P.COD_DECK_FC
union all
select P.COD_DECK_IC, P.TAG_USR_IC, COUNT(P.COD_DECK_IC)
from partita P
group by P.TAG_USR_IC, P.COD_DECK_IC
)
select ct.*
from (select ct.*,
row_number() over (partition by usr order by cnt desc) as seqnum
from ct
) ct
where seqnum = 1;
You can also shorten this using grouping sets:
select p.*
from (select coalesce(P.COD_DECK_FC, P.COD_DECK_IC) as deck,
coalesce(P.TAG_USR_FC, P.TAG_USR_IC) as usr,
count(*) as cnt,
row_number() over (partition by coalesce(P.TAG_USR_FC, P.TAG_USR_IC) order by count(*) desc) as seqnum
from partita p
group by grouping sets ( (P.TAG_USR_FC, P.COD_DECK_FC), P.TAG_USR_IC, P.COD_DECK_IC) )
) p
where seqnum = 1;

SQL - Oracle - Duplicate IDs with more than one Customer Name value into a separate column

Please help me determine a "good" or best way of setting up this query, of course, if it will work. = )
Below is an example and basic query that provides CustomerID, Cust Name, UserName and Email. I'm trying to take the multiple username/email fields for one CustID and placing them in a new/separate field so that I have a distinct CustomerID.
An example of the current data is:
Cust_Nbr Cust_Name User_Name Email
0011 Customer11 User1a Email1a
0011 Customer11 User1b Email1b
Trying to achieve:
Cust_Nbr Cust_Name User_Name Email UserName2 Email2
0011 Customer11 User1a Email1a User1b Email1b
The sql is similar to this:
SELECT
cust.CUST_ID,
cust.CUST_NAME,
orders.NAME Orders_Name1,
orders.EMAIL EMAIL1
FROM
CUSTOMER cust,
ORDERS_USERS orders,
SALES_TRANS sls
WHERE
etc...
GROUP BY
etc...
I tried devising a way to do a union copying the select statement 3x and then trying to separate the values with new alias column names, e.g., username1, username2, etc., but that didn't work very well.
Any suggestions? Should I use a count statement with an indicator and then group those as sub selects? Not too sure.
Also, I am familiar with SQL Server format, but please keep in mind this is Oracle sql. Thanks!
It isn't good solution, but if you have known rows you may use:
with tb (Cust_Nbr,Cust_Name,User_Name,Email) as (
select '0011', 'Customer11', 'User1a', 'Email1a' from dual union all
select '0011', 'Customer11', 'User1b', 'Email1b' from dual)
select Cust_Nbr,Cust_Name,
max(decode(user_rn,1,User_Name)) as User1,
max(decode(user_rn,2,User_Name)) as User2,
max(decode(email_rn,1,User_Name)) as Email1,
max(decode(email_rn,2,User_Name)) as Email2
from
(select Cust_Nbr,Cust_Name,User_Name,Email,
row_number() over (partition by Cust_Nbr,Cust_Name order by User_Name) as user_rn,
row_number() over (partition by Cust_Nbr,Cust_Name order by Email) as email_rn
from tb)
group by Cust_Nbr,Cust_Name
first of all i would strongly recommend you to redesign your table(s) and split the data into two tables "customer" and "email" (customer_id, ...) - this will make your life much easier. But if you can't change it here is an answer:
create table customer(id int, name varchar2(100), username varchar2(100), email varchar2(100));
insert into customer
select 1,'Customer11', 'User1a', 'Email1a#mail.com' from dual
union
select 1,'Customer11', 'User1b', 'Email1b#mail.com' from dual
union
select 1,'Customer11', 'User1c', 'Email1c#mail.com' from dual
union
select 2,'Customer12', 'User12a', 'Email12a#mail.com' from dual
;
commit;
select id, name, listagg('<'||username||'> '|| email, ', ') WITHIN GROUP (ORDER BY id)
from customer group by id, name;
Output:
ID NAME USERNAME_EMAIL
1 Customer11 <User1a> Email1a#mail.com, <User1b> Email1b#mail.com, <User1c> Email1c#mail.com
2 Customer12 <User12a> Email12a#mail.com

PostgreSQL 9.3: Pivot table query

I want to show the pivot table(crosstab) for the given below table.
Table: Employee
CREATE TABLE Employee
(
Employee_Number varchar(10),
Employee_Role varchar(50),
Group_Name varchar(10)
);
Insertion:
INSERT INTO Employee VALUES('EMP101','C# Developer','Group_1'),
('EMP102','ASP Developer','Group_1'),
('EMP103','SQL Developer','Group_2'),
('EMP104','PLSQL Developer','Group_2'),
('EMP101','Java Developer',''),
('EMP102','Web Developer','');
Now I want to show the pivot table for the above data as shown below:
Expected Result:
Employee_Number TotalRoles TotalGroups Available Others Group_1 Group_2
---------------------------------------------------------------------------------------------------
EMP101 2 2 1 1 1 0
EMP102 2 2 1 1 1 0
EMP103 1 2 1 0 0 1
EMP104 1 2 1 0 0 1
Explanation: I want to show the Employee_Number, the TotalRoles which each employee has,
the TotalGroups which are present to all employees, the Available shows the employee available
in how many groups, the Others have to show the employee is available in other's also for which
the group_name have not assigned and finally the Group_Names must be shown in the pivot format.
SELECT * FROM crosstab(
$$SELECT grp.*, e.group_name
, CASE WHEN e.employee_number IS NULL THEN 0 ELSE 1 END AS val
FROM (
SELECT employee_number
, count(employee_role)::int AS total_roles
, (SELECT count(DISTINCT group_name)::int
FROM employee
WHERE group_name <> '') AS total_groups
, count(group_name <> '' OR NULL)::int AS available
, count(group_name = '' OR NULL)::int AS others
FROM employee
GROUP BY 1
) grp
LEFT JOIN employee e ON e.employee_number = grp.employee_number
AND e.group_name <> ''
ORDER BY grp.employee_number, e.group_name$$
,$$VALUES ('Group_1'::text), ('Group_2')$$
) AS ct (employee_number text
, total_roles int
, total_groups int
, available int
, others int
, "Group_1" int
, "Group_2" int);
SQL Fiddle demonstrating the base query, but not the crosstab step, which is not installed on sqlfiddle.com
Basics for crosstab:
PostgreSQL Crosstab Query
Special in this crosstab: all the "extra" columns. Those columns are placed in the middle, after the "row name" but before "category" and "value":
Pivot on Multiple Columns using Tablefunc
Once again, if you have a dynamic set of groups, you need to build this statement dynamically and execute it in a second call:
Selecting multiple max() values using a single SQL statement
You can use the crosstab function for this.
First of all you need to add the tablefunc extension if you haven't already:
CREATE EXTENSION tablefunc;
The crosstab functions require you to pass it a query returning the data you need to pivot, then a list of the columns in the output. (In other ways "tell me the input and the output format you want"). The sort order is important!
In your case, the input query is quite complicated - I think you need to do a load of separate queries, then UNION ALL them to get the desired data. I'm not entirely sure how you calculate the values "TotalGroups" and "Available", but you can modify the below in the relevant place to get what you need.
SELECT * FROM crosstab(
'SELECT employee_number, attribute, value::integer AS value FROM (with allemployees AS (SELECT distinct employee_number FROM employee) -- use a CTE to get distinct employees
SELECT employee_number,''attr_0'' AS attribute,COUNT(distinct employee_role) AS value FROM employee GROUP BY employee_number -- Roles by employee
UNION ALL
SELECT employee_number,''attr_1'' AS attribute,value from allemployees, (select count (distinct group_name) as value from employee where group_name <> '''') a
UNION ALL
SELECT employee_number,''attr_2'' AS attribute, COUNT(distinct group_name) AS value FROM employee where group_name <> '''' GROUP BY employee_number -- Available, do not know how this is calculate
UNION ALL
SELECT a.employee_number, ''attr_3'' AS attribute,coalesce(value,0) AS value FROM allemployees a LEFT JOIN -- other groups. Use a LEFT JOIN to avoid nulls in the output
(SELECT employee_number,COUNT(*) AS value FROM employee WHERE group_name ='''' GROUP BY employee_number) b on a.employee_number = b.employee_number
UNION ALL
SELECT a.employee_number, ''attr_4'' AS attribute,coalesce(value,0) AS value FROM allemployees a LEFT JOIN -- group 1
(SELECT employee_number,COUNT(*) AS value FROM employee WHERE group_name =''Group_1'' GROUP BY employee_number) b on a.employee_number = b.employee_number
UNION ALL
SELECT a.employee_number, ''attr_5'' AS attribute,coalesce(value,0) AS value FROM allemployees a LEFT JOIN -- group 2
(SELECT employee_number,COUNT(*) AS value FROM employee WHERE group_name =''Group_2'' GROUP BY employee_number) b on a.employee_number = b.employee_number) a order by 1,2')
AS ct(employee_number varchar,"TotalRoles" integer,"TotalGroups" integer,"Available" integer, "Others" integer,"Group_1" integer, "Group_2" integer)

Find the most recent shipment for a product (sql subselect?)

I have three tables
shipment (shipment_id, shipping_date)
company_order (company_order_id, shipment_id, company_id)
company_order_item (company_order_item_id, company_order_id, product_id)
Several companies get together and aggregate orders from a single manufacturer. This aggregate order is called a "shipment". Companies order a selection of products in each shipment: so not all products will be present in any one shipment or for any one company.
How do I write an SQL query find the most recent shipment for each product_id ?
I've looked at
SQL Query - Get Most Recent Revision (much simpler case).
You need to get the max shipment date per product id and then retrieve the shipment detaisl
Something like
SELECT *
FROM (
SELECT coi.product_id,
MAX(s.shipping_date) MaxDate
FROM company_order_item coi INNER JOIN
company_order co ON coi.company_order_id = co.company_order_id INNER JOIN
shipment s ON co.shipment_id =s.shipment_id
GROUP BY coi.product_id
) sub INNER JOIN
company_order_item coi ON sub.product_id = coi.product_id INNER JOIN
company_order co ON coi.company_order_id = co.company_order_id INNER JOIN
shipment s ON co.shipment_id = s.shipment_id
AND s.shipping_date = sub.MaxDate
SQL Code to illustrate - (This is T-SQL and is SQL Server friendly, but i didn't have any mysql handy. The last query should with tiny modifications (to suit your table names) work nicely in MySQL as well.
My logic is to find the most recent company_order for each product_id. Once i have that i can just join the company_order_id to company_order, and i have the shipment_id for each most-recent company_order per product_id
DROP TABLE #shipment
DROP TABLE #company_order
DROP TABLE #company_order_item
CREATE TABLE #shipment
(
shipment_id INT ,
shipping_date INT
) ;
CREATE TABLE #company_order
(
company_order_id INT ,
shipment_id INT ,
company_id INT
) ;
CREATE TABLE #company_order_item
(
company_order_item_id INT ,
company_order_id INT ,
product_id INT
) ;
INSERT INTO #shipment
( shipment_id , shipping_date )
VALUES
( 1 , 1 ),
( 2 , 2 ),
( 3 , 3 )
INSERT INTO #company_order
( company_order_id , shipment_id , company_id )
VALUES
( 1 , 1 , 1 ),
( 2 , 2 , 1 ),
( 3 , 3 , 1 )
INSERT INTO #company_order_item
( company_order_item_id , company_order_id , product_id )
VALUES
( 1 , 1 , 1 ) ,
( 2 , 1 , 2 ),
( 2 , 2 , 2 ),
( 1 , 1 , 3 ),
( 1 , 3 , 4 )
SELECT
product_id ,
shipment_id
FROM
(
SELECT
product_id ,
MAX(company_order_id) AS company_order_id
FROM
#company_order_item
GROUP BY
product_id
) AS MostRecentProductInOrder
INNER JOIN #company_order
ON MostRecentProductInOrder.company_order_id = #company_order.company_order_id