I have 3 tables for example
Parent Table :TEST_SUMMARY
Child Tables : TEST_DETAIL, TEST_DETAIL2
I have data show in image, and want output result shown in image,
I tried below 2 query, but not giving expected output
SELECT s.NAME, sum(s.AMT), sum(d.d_amt), sum(d2.d2_amt)
FROM TEST_SUMMARY s LEFT OUTER JOIN TEST_DETAIL d
ON s.ID = d.SUMMARY_ID
LEFT OUTER JOIN TEST_DETAIL2 d2
ON s.ID =d2.SUMMARY_ID
GROUP BY s.NAME
ORDER BY s.NAME;
select rs1.*,rs2.total1,rs3.total2
FROM
(select id, name,amt from TEST_SUMMARY a) RS1,
(select SUMMARY_ID, sum(d_amt) over(partition by summary_id ) total1 from TEST_DETAIL a) RS2,
(select SUMMARY_ID, sum(d2_amt) over(partition by summary_id ) total2 from TEST_DETAIL2 a) RS3
where rs1.id(+)= RS2.SUMMARY_ID
and rs1.id(+)= RS3.SUMMARY_ID;
Create table and insert data test Queries
CREATE TABLE TEST_SUMMARY(ID NUMBER, NAME VARCHAR2(20 BYTE),AMT NUMBER(10,2));
CREATE TABLE TEST_DETAIL (ID NUMBER, SUMMARY_ID NUMBER, NAME VARCHAR(20), D_AMT NUMBER(10,2));
CREATE TABLE TEST_DETAIL2 (ID NUMBER, SUMMARY_ID NUMBER, NAME VARCHAR(20), D2_AMT NUMBER(10,2));
INSERT INTO TEST_SUMMARY VALUES (1, 'NAME1', 100);
INSERT INTO TEST_SUMMARY VALUES (4, 'NAME1', 150);
INSERT INTO TEST_SUMMARY VALUES (6, 'NAME1', 50);
INSERT INTO TEST_SUMMARY VALUES (2, 'NAME2', 200);
INSERT INTO TEST_SUMMARY VALUES (3, 'NAME3', 300);
INSERT INTO TEST_DETAIL VALUES (1, 1, 'NAME11', 11);
INSERT INTO TEST_DETAIL VALUES (2, 1, 'NAME12', 12);
INSERT INTO TEST_DETAIL2 VALUES (1, 1, 'NAME_2_11', 1);
INSERT INTO TEST_DETAIL2 VALUES (2, 1, 'NAME_2_12', 1);
One way to solve it for both MySQL and Oracle is to use subqueries to help solve the duplication for you by aggregating the sums from the details tables by name, so you can summarise with a normal join;
SELECT ts.name, SUM(ts.amt) amt1, MAX(td1.amt) amt2, MAX(td2.amt) amt3
FROM TEST_SUMMARY ts
LEFT JOIN (
SELECT ts.name, SUM(td.d_amt) amt
FROM TEST_DETAIL td JOIN TEST_SUMMARY ts ON td.summary_id = ts.id
GROUP BY ts.name) td1 ON ts.name = td1.name
LEFT JOIN (
SELECT ts.name, SUM(td.d2_amt) amt
FROM TEST_DETAIL2 td JOIN TEST_SUMMARY ts ON td.summary_id = ts.id
GROUP BY ts.name) td2 ON ts.name = td2.name
GROUP BY ts.name
ORDER BY ts.name
A MySQL SQLfiddle and an Oracle SQLfiddle to test with.
You could try this:
SELECT
TEST_SUMMARY.NAME,
TEST_SUMMARY.AMT AS AMT1,
(
SELECT
SUM(TEST_DETAIL.D_AMT)
FROM
TEST_DETAIL
WHERE
TEST_DETAIL.SUMMARY_ID=TEST_SUMMARY.ID
) AS AMT2,
(
SELECT
SUM(TEST_DETAIL2.D2_AMT)
FROM
TEST_DETAIL2
WHERE
TEST_DETAIL2.SUMMARY_ID=TEST_SUMMARY.ID
) AS AMT3
FROM
TEST_SUMMARY
Update
You could basically do this if you have many name that are the same. But the question comes what you should do with the other fields (AMT1,AMT2)? Should you sum them for the same name or maybe a max is enough. Depends on what your requirement are :
SELECT
TEST_SUMMARY.NAME,
SUM(TEST_SUMMARY.AMT) AS AMT,
SUM(tblAMT2.AMT2) AS AMT2,
SUM(tblAMT3.AMT3) AS AMT3
FROM
TEST_SUMMARY
LEFT JOIN
(
SELECT
SUM(TEST_DETAIL.D_AMT) AS AMT2,
TEST_DETAIL.SUMMARY_ID
FROM
TEST_DETAIL
GROUP BY
TEST_DETAIL.SUMMARY_ID
) AS tblAMT2
ON TEST_SUMMARY.ID=tblAMT2.SUMMARY_ID
LEFT JOIN
(
SELECT
SUM(TEST_DETAIL2.D2_AMT) AS AMT3,
TEST_DETAIL2.SUMMARY_ID
FROM
TEST_DETAIL2
GROUP BY
TEST_DETAIL2.SUMMARY_ID
) AS tblAMT3
ON TEST_SUMMARY.ID=tblAMT3.SUMMARY_ID
GROUP BY
TEST_SUMMARY.NAME
Try this:
SELECT TS.NAME, TS.AMT AS AMT1, SUM(TD.D_AMT) AS AMT2, SUM(TD2.D2_AMT) AS AMT3
FROM TEST_SUMMARY TS LEFT OUTER JOIN TEST_DETAIL TD ON TS.ID = TD.SUMMARY_ID
LEFT OUTER JOIN TEST_DETAIL2 TD2 ON TS.ID = TD2.SUMMARY_ID
GROUP BY TS.NAME, TS.AMT
ORDER BY TS.NAME, TS.AMT
Related
I am trying to write a query in MySQL that will output the most frequently occurring pair of values. I have the following table:
Original Dataset
This table contains users' music streaming activity on a given day. I want to find out which pair of artists was the most frequently played one on a specific day. The answer should be (Pink Floyd, Queen) because 3 users listened to both artists on the same day. How can I achieve this?
I've started by joining the table onto itself using this code:
With temp as (
select person_id, artist_name, count(*) as times_played from users where date_played = '2020-10-01' group by 1,2)
select a.person_id, a.artist_name, b.artist_name from temp a join temp b
On a.person_id = b.person_id and a.artist_name != b. artist_name;
The result is the following:
I am not sure how to process from this point, so any help would be appreciated!
Below is the code to create the table in mySQL
create table users
(
person_id int,
artist_name varchar(255),
date_played date
);
insert into users
(person_id, artist_name, date_played)
values
(1, 'Pink Floyd', '2020-10-01'),
(1, 'Led Zeppelin', '2020-10-01'),
(1, 'Queen', '2020-10-01'),
(1, 'Pink Floyd', '2020-10-01'),
(2, 'Journey', '2020-10-01'),
(2, 'Pink Floyd', '2020-10-01'),
(2, 'Queen', '2020-10-01'),
(2, 'Pink Floyd', '2020-10-01'),
(3, 'Pink Floyd', '2020-10-01'),
(3, 'Aerosmith', '2020-10-01'),
(3, 'Queen', '2020-10-01'),
(4, 'Pink Floyd', '2020-10-01'),
(4, 'Led Zeppelin', '2020-10-01');
Here's how I solved my question thanks to the trick I found in the code provided by Tim Biegeleisen in this post (u1.artist_name < u2.artist_name):
With temp AS (
SELECT
person_id,
artist_name
FROM users
WHERE date_played = '2020-10-01'
GROUP BY 1,2
)
SELECT *
FROM (
SELECT
u1.artist_name AS artist1,
u2.artist_name AS artist2,
COUNT(*) AS times_played,
RANK() OVER (ORDER BY COUNT(*) DESC) Rnk
FROM temp u1
JOIN temp u2
ON u1.artist_name < u2.artist_name AND u1.person_id = u2.person_id
GROUP by 1,2
) sub
WHERE Rnk = 1;
We can try handling this requirement using a self join along with the RANK() analytic function:
WITH cte AS (
SELECT
u1.artist_name AS artist1,
u2.artist_name AS artist2,
RANK() OVER (ORDER BY COUNT(*) DESC) rnk
FROM users u1
INNER JOIN users u2
ON u1.artist_name < u2.artist_name AND u1.person_id = u2.person_id
WHERE
u1.date_played = u2.date_played
GROUP BY
u1.artist_name,
u2.artist_name
)
SELECT
artist1,
artist2
FROM cte
WHERE rnk = 1;
I have two tables, 'team' and 'tickets'. Right now its displaying all records and also which ticket the team member is dispatched too.
I now need to count the number of tickets for each team.
http://sqlfiddle.com/#!9/609d4f/18
Here's my attempt:
SELECT
team.techid,
team.name,
tickets.techid,
tickets.customer,
tickets.callstatus,
tickets.serialnumber
FROM team
LEFT JOIN tickets
ON tickets.techid = team.techid AND (tickets.callstatus = 'Dispatch') AND
(COUNT(tickets.customer) WHERE tickets.techid = team.techid )
Update
Working example but only missing the count column:
http://sqlfiddle.com/#!9/609d4f/19
Update2
Tim, thank you for your help but your example doesn't work.
The table should look like this, minus the missing columns of course:
|---------------------|------------------|
| Tech ID | Count |
|---------------------|------------------|
| Tech1 | 1 |
|---------------------|------------------|
| Tech2 | 1 |
|---------------------|------------------|
| Tech3 | 0 |
|---------------------|------------------|
http://sqlfiddle.com/#!9/bfcdf5/1
As you see below, tech1 and tech2 both have records in ato_openservicecalls where the SC_CallStatus is Dispatch
insert into `serviceteam` VALUES (1, 'tech1', 'name1', 'manager1', 'dispatcher1', 'cellphone1');
insert into `serviceteam` VALUES (2, 'tech2', 'name2', 'manager2', 'dispatcher2', 'cellphone2');
insert into `serviceteam` VALUES (3, 'tech3', 'name3', 'manager3', 'dispatcher3', 'cellphone3');
insert into `ato_openservicecalls` VALUES (1, 'tech1', 'Dispatch', 'customer1', 'age1', 'timestamp1', 'serial1', 'comment1');
insert into `ato_openservicecalls` VALUES (2, 'tech2', 'Dispatch', 'customer2', 'age2', 'timestamp2', 'serial2', 'comment2');
insert into `ato_openservicecalls` VALUES (3, 'tech3', 'callstatus3', 'customer3', 'age3', 'timestamp3', 'serial3', 'comment3');
SELECT
t1.techid,
t1.techname,
t1.manager,
t1.dispatcher,
t1.cellphone,
t2.SC_SCTechID,
t2.BCARNA,
t2.SC_CallStatus,
t2.Serial_ID,
t2.Age,
t2.SC_CallTimestamp,
t2.SC_CallComment,
COALESCE(t3.num_tickets, 0) AS num_tickets
FROM serviceteam t1
LEFT JOIN ato_openservicecalls t2
ON t1.techid = t2.SC_SCTechID AND t2.SC_CallStatus = 'Dispatch'
LEFT JOIN
(
SELECT t1.techid, COUNT(*) AS num_tickets
FROM serviceteam t1
INNER JOIN ato_openservicecalls t2
ON t1.techid = t2.SC_SCTechID
WHERE t2.SC_CallStatus = 'Dispatch'
) t3
ON t1.techid = t3.techid;
In MySQL versions earlier than 8+, we can find the counts using a subquery and then join to it:
SELECT
t1.techid,
t1.name,
t2.techid,
t2.customer,
t2.callstatus,
t2.serialnumber,
COALESCE(t3.num_tickets, 0) AS num_tickets
FROM team t1
LEFT JOIN tickets t2
ON t1.techid = t2.techid AND t2.callstatus = 'Dispatch'
LEFT JOIN
(
SELECT t1.techid, COUNT(*) AS num_tickets
FROM team t1
INNER JOIN tickets t2
ON t1.techid = t2.techid
WHERE t2.callstatus = 'Dispatch'
) t3
ON t1.techid = t3.techid;
With MySQL 8+ or later, we can take advantage of analytic functions:
SELECT
t1.techid,
t1.name,
t2.techid,
t2.customer,
t2.callstatus,
t2.serialnumber,
COUNT(t2.customer) OVER (PARTITION BY t1.techid) num_tickets
FROM team t1
LEFT JOIN tickets t2
ON t1.techid = t2.techid AND t2.callstatus = 'Dispatch';
Edit:
You completely changed your question, invalidating my first accepted answer. Here is the new query:
SELECT
t1.techid,
COUNT(t2.customer) AS num_tickets
FROM team t1
LEFT JOIN tickets t2
ON t1.techid = t2.techid AND t2.callstatus = 'Dispatch'
GROUP BY
t1.techid;
I have two tables:
INSERT INTO `companies` (`name`) VALUES
('Walmart'),
('Disney'),
('Amazon'),
('Unicom'),
('Microsoft'),
('Intel')
INSERT INTO `users` (`id`, `company`) VALUES
(1, 'Disney'),
(2, 'Amazon'),
(3, 'Intel'),
(3, 'Walmart'),
(4, 'Microsoft'),
(4, 'Unicom'),
(5, 'Microsoft')
The result should be following:
1. 'Walmart', 'Amazon', 'Unicom', 'Microsoft', 'Intel'
2. 'Walmart', 'Disney', 'Unicom', 'Microsoft', 'Intel'
3. 'Disney', 'Amazon', 'Unicom', 'Microsoft'
4. 'Walmart', 'Disney', 'Amazon', 'Intel'
5. 'Walmart', 'Disney', 'Amazon', 'Unicom', 'Intel'
I have tried with:
"SELECT a.name, b.id, b.company FROM users RIGHT JOIN companies ON b.company <> a.name"
This gives the correct logic by omitting the company name that's already on the list but the problem is that it processes the same id twice and omits a different company name. How would one approach this query?
The basic idea in the query below is to left join a calendar table containing every possible user/company combination to the users table. Those combinations which do match are removed, and the remaining companies are then rolled up into a CSV string for each user using GROUP_CONCAT.
SELECT t1.id, GROUP_CONCAT(t1.name)
FROM
(
SELECT DISTINCT u.id, c.name
FROM users u
CROSS JOIN companies c
) t1
LEFT JOIN users t2
ON t1.name = t2.company AND t1.id = t2.id
WHERE t2.company IS NULL
GROUP BY
t1.id;
Demo
Try this
"SELECT a.name, b.id, b.company FROM users RIGHT JOIN companies ON b.company <> a.name group by b.company"
Given two tables:
The 'people' table contains the following columns:
name
favorite_walking_shoe
favorite_running_shoe
favorite_dress_shoe
favorite_house_shoe
favorite_other_shoe
The 'shoes' table contains the following columns:
shoe
name
description
I want to create a result set that contains:
people.name, people.favorite_shoe_type, shoes.name, shoes.description
I know I can get the desired results using something like:
select p.name, p.favorite_shoe_type, s.name, s.description
from (select name, favorite_walking_shoe as shoe, 'walking' as favorite_shoe_type
from people where favorite_walking_shoe is not null
union all
select name, favorite_running_shoe, 'running'
from people where favorite_running_shoe is not null
union all
select name, favorite_dress_shoe, 'dress'
from people where favorite_dress_shoe not is null
union all
select name, favorite_house_shoe, 'house'
from people where favorite_house_shoe not is null
union all
select name, favorite_other_shoe, 'other'
from people where favorite_other_shoe not is null
) p
join shoes s on s.shoe = p.shoe
order by 1,2
but this would require 5 passes of the 'people' table. Is there a way to accomplish the UNION ALLs without requiring multiple passes?
I should point out that the structures are part of a vendor product which I cannot modify. :(
You can get around the five scans by doing a cross join:
select p.name, p.favorite_shoe_type, s.name, s.description
from (select p.*,
(case when favorite_shoetype = 'walking' then p.favore_walking_shoe
when favorite_shoetype = 'running' then p.favorite_running_shoe
when favorite_shoetype = 'dress' then p.favorite_dress_shoe
when favorite_shoetype = 'house' then p.favorite_house_shoe
when favorite_shoetype = 'other' then p.favorite_other_shoe
end) as shoe
from people p cross join
(select 'walking' as favorite_shoe_type union all
select 'running' union all
select 'dress' union all
select 'house' union all
select 'other'
) shoetypes join
shoes s
) p
on s.shoe = p.shoe
I'm not sure this will be more efficient. If you have indexes on shoe, this even more complicated version might be more efficient:
select p.name, p.favorite_shoe_type, s.name, s.description
from (select p.name, favorite_shoe_types,
(case when favorite_shoetype = 'walking' then ws.name
when favorite_shoetype = 'running' then rs.name
when favorite_shoetype = 'dress' then ds.name
when favorite_shoetype = 'house' then hs.name
when favorite_shoetype = 'other' then os.name
end) as name,
(case when favorite_shoetype = 'walking' then ws.description
when favorite_shoetype = 'running' then rs.description
when favorite_shoetype = 'dress' then ds.description
when favorite_shoetype = 'house' then hs.description
when favorite_shoetype = 'other' then os.name
end) as description
from people p left outer join
shoes ws
on ws.shoe = favorite_walking_shoe left outer join
shoes rs
on rs.shoe = favorite_running_shoe left outer join
shoes ds
on ds.shoe = favorite_dress_shoe left outer join
shoes hs
on hs.shoe = favorite_house_shoe left outer join
shoes os
on os.shoe = favorite_other_shoe cross join
(select 'walking' as favorite_shoe_type union all
select 'running' union all
select 'dress' union all
select 'house' union all
select 'other'
) shoetypes
) p
on s.shoe = p.shoe
where s.name is not null
This should do the five joins using indexes -- quite fast, one scan of the people table, and feed this to the cross join. The logic then returns the values that you want.
Note: both of these are untested so they might have syntax errors.
Unfortunately, the way your current table is structured you will have multiple passes to get each value. If it is possible, I would suggest changing your table structure to the include a shoe_type table and then a join table between the people and shoes and in this table, you can include a flag that will show if the shoe is the favorite.
So it will be similar to this:
create table people_shoe
(
people_id int,
shoe_id int,
IsFavorite int
);
You could also have a shoe_type table to store each of the different show types:
create table shoe_type
(
id int,
name varchar(10)
);
insert into shoe_type
values('Walking'), ('Running'), ('Dress'), ('House'), ('Other');
The shoe_type.id would be added to your shoe table and you would join the tables.
Edit #1, if you can remodel the database, you could use the following (mock-up model):
create table people
(
id int,
name varchar(10)
);
insert into people values (1, 'Jim'), (2, 'Jane');
create table shoe_type
(
id int,
name varchar(10)
);
insert into shoe_type
values(1, 'Walking'), (2, 'Running'), (3, 'Dress'), (4, 'House'), (5, 'Other');
create table shoes
(
id int,
name varchar(10),
description varchar(50),
shoe_type_id int
);
insert into shoes
values(1, 'Nike', 'test', 2), (2, 'Cole Haan', 'blah', 3);
create table people_shoe
(
people_id int,
shoe_id int,
IsFavorite int
);
insert into people_shoe
values (1, 1, 1),
(1, 2, 0),
(2, 1, 1);
Then when you query, your code will be similar to this:
select p.name PersonName,
s.name ShoeName,
st.name ShoeType,
ps.isFavorite
from people p
inner join people_shoe ps
on p.id = ps.people_id
inner join shoes s
on ps.shoe_id = s.id
inner join shoe_type st
on s.shoe_type_id = st.id
See SQL Fiddle with Demo
I have structure like this:
<Unit>
<SubUnit1>
<SubSubUnit1/>
<SubSubUnit2/>
...
<SubSubUnitN/>
</SubUnit1/>
<SubUnit2>
<SubSubUnit1/>
<SubSubUnit2/>
...
<SubSubUnitN/>
</SubUnit2/>
...
<SubUnitN>
<SubSubUnit1/>
<SubSubUnit2/>
...
<SubSubUnitN/>
</SubUnitN/>
</Unit>
This structure has 3 levels: main Unit, SubUnits and SubSubUnits.
I want to select all children by UnitId.
If I search by Unit, I have to get all tree.
If I search by SubUnit1, I have to get SubUnit1 and all children of SubUnit1.
If I search SubSubUnit2, I have to get itself.
Here is my try:
with a(id, parentid, name)
as (
select id, parentId, name
from customer a
where parentId is null
union all
select a.id, a.parentid, a.Name
from customer
inner join a on customer.parentId = customer.id
)
select parentid, id, name
from customer pod
where pod.parentid in (
select id
from customer grbs
where grbs.parentid in (
select id
from customer t
where t.parentid = #UnitId
))
union
select parentid, id, name
from customer grbs
where grbs.parentid in (
select id
from customer t
where t.parentid = #UnitId
)
union
select parentid, id, name
from customer c
where c.Id = #UnitId
order by parentid, id
I use 3 union-words, it is not well but it works. Case structure will have N levels, how I have to get correct result?
DECLARE #Id int = your_UnitId
;WITH cte AS
(
SELECT a.Id, a.parentId, a.name
FROM customer a
WHERE Id = #Id
UNION ALL
SELECT a.Id, a.parentid, a.Name
FROM customer a JOIN cte c ON a.parentId = c.id
)
SELECT parentId, Id, name
FROM cte
Demo on SQLFiddle
In case of parent id is a child of itself then we need to use a different query. For example, schema structure is like below
CREATE TABLE customer
(
id int,
parentid int,
name nvarchar(10)
)
INSERT customer
VALUES(1, 1, 'aaa'),
(2, 1, 'bbb'),
(3, 2, 'ccc'),
(4, 2, 'ddd'),
(5, 1, 'eee'),
(6, 5, 'fff'),
(7, 5, 'ggg'),
(8, 8, 'hhh'),
(9, 8, 'iii'),
(10, 8, 'jjj')
In this case, we need to use below query:
DECLARE #Id int = 1 -- your UnitId
;WITH cte AS
(
SELECT a.Id, a.parentId, a.name
FROM customer a
WHERE parentid = #Id
UNION ALL
SELECT a.Id, a.parentid, a.Name
FROM customer a JOIN cte c ON a.parentId = c.id
and c.id != #Id
)
SELECT parentId, Id, name
FROM cte
go