Select sum shown null value in left join SQL statement - sql-server-2000

I have three tables and expecting the result as below but i do not know how to correct my sql statement.
select history.company,history.ghacct,rpt_revenue.revenue,rpt_revenue.other, isnull(guest.stay,0) as stay, isnull(guest.nights,0) as nights
from history
left join(select company,count(*) as stay,sum(nights) as nights from guest group by company) guest on guest.company=history.company
left join (select ghacct,sum(revenue) as revenue, sum(other) as other
from rpt_revenue group by ghacct) rpt_revenue on rpt_revenue.ghacct=history.ghacct
where history.type='c' group by history.company, history.ghacct,rpt_revenue.revenue, rpt_revenue.other,guest.stay,guest.nights order by history.company asc;
history
ghacct company type
33 JOINT LTD 10010205687 c
3B GLOBAL 10010350619 c
3E FASHION 10010244145 c
3P INT'L 10010112089 c
guest
company stay nights
33 JOINT LTD 01/01/2009 1
33 JOINT LTD 01/06/2009 1
3B GLOBAL 10/02/2019 2
3E FASHION 09/25/2008 6
3P INT'L 08/26/2009 3
3P INT'L 04/26/2010 9
rpt_revenue
ghacct revenue other
10010205687 20 10
10010205687 10 10
10010350619 30 2
10010244145 15 3
10010112089 16 8
10010112089 4 2
Result
company ghacct revenue other stay nights
33 JOINT LTD 10010205687 NULL NULL 2 2
3B GLOBAL 10010350619 NULL NULL 1 2
3E FASHION 10010244145 NULL NULL 1 6
3P INT'L 10010112089 NULL NULL 2 12
Expected result
company ghacct revenue other stay nights
33 JOINT LTD 10010205687 30 20 2 2
3B GLOBAL 10010350619 30 2 1 2
3E FASHION 10010244145 15 3 1 6
3P INT'L 10010112089 20 10 2 12

I think the main problem with your current query lies in the GROUP BY clause, which should really only be aggregating by company and account. In addition, you might want to use ISNULL for the revenue and other amount, as you are already doing so for stay and nights.
SELECT
h.company,
h.ghacct,
ISNULL(rr.revenue, 0) AS revenue,
ISNULL(rr.other, 0) AS other,
ISNULL(g.stay, 0) AS stay,
ISNULL(g.nights, 0) AS nights
FROM history h
LEFT JOIN
(
SELECT company, COUNT(*) AS stay, SUM(nights) AS nights
FROM guest
GROUP BY company
) g
ON g.company = h.company
LEFT JOIN
(
SELECT ghacct, SUM(revenue) AS revenue, SUM(other) AS other
FROM rpt_revenue
GROUP BY ghacct
) rr
ON rr.ghacct = h.ghacct
WHERE
h.type = 'c'
GROUP BY
h.company,
h.ghacct
ORDER BY
h.company;

Related

How to get top values when there is a tie

I am having difficulty figuring out this dang problem. From the data and queries I have given below I am trying to see the email address that has rented the most movies during the month of September.
There are only 4 relevant tables in my database and they have been anonymized and shortened:
Table "cust":
cust_id
f_name
l_name
email
1
Jack
Daniels
jack.daniels#google.com
2
Jose
Quervo
jose.quervo#yahoo.com
5
Jim
Beam
jim.beam#protonmail.com
Table "rent"
inv_id
cust_id
rent_date
10
1
9/1/2022 10:29
11
1
9/2/2022 18:16
12
1
9/2/2022 18:17
13
1
9/17/2022 17:34
14
1
9/19/2022 6:32
15
1
9/19/2022 6:33
16
3
9/1/2022 18:45
17
3
9/1/2022 18:46
18
3
9/2/2022 18:45
19
3
9/2/2022 18:46
20
3
9/17/2022 18:32
21
3
9/19/2022 22:12
10
2
9/19/2022 11:43
11
2
9/19/2022 11:42
Table "inv"
mov_id
inv_id
22
10
23
11
24
12
25
13
26
14
27
15
28
16
29
17
30
18
31
19
31
20
32
21
Table "mov":
mov_id
titl
rate
22
Anaconda
3.99
23
Exorcist
1.99
24
Philadelphia
3.99
25
Quest
1.99
26
Sweden
1.99
27
Speed
1.99
28
Nemo
1.99
29
Zoolander
5.99
30
Truman
5.99
31
Patient
1.99
32
Racer
3.99
and here is my current query progress:
SELECT cust.email,
COUNT(DISTINCT inv.mov_id) AS "Rented_Count"
FROM cust
JOIN rent ON rent.cust_id = cust.cust_id
JOIN inv ON inv.inv_id = rent.inv_id
JOIN mov ON mov.mov_id = inv.mov_id
WHERE rent.rent_date BETWEEN '2022-09-01' AND '2022-09-31'
GROUP BY cust.email
ORDER BY "Rented_Count" DESC;
and here is what it outputs:
email
Rented_Count
jack.daniels#google.com
6
jim.beam#protonmail.com
6
jose.quervo#yahoo.com
2
and what I want it to be outputting:
email
jack.daniels#google.com
jim.beam#protonmail.com
From the results I am actually getting I have a tie for first place (Jim and Jack) and that is fine but I would like it to list both tieing email addresses not just Jack's so you cant do anything with rows or max I don't think.
I think it must have something to do with dense_rank but I don't know how to use that specifically in this scenario with the count and Group By?
Your creativity and help would be appreciated.
You're missing the FETCH FIRST ROWS WITH TIES clause. It will work together with the ORDER BY clause to get you the highest values (FIRST ROWS), including ties (WITH TIES).
SELECT cust.email
FROM cust
INNER JOIN rent
ON rent.cust_id = cust.cust_id
INNER JOIN inv
ON inv.inv_id = rent.inv_id
INNER JOIN mov
ON mov.mov_id = inv.mov_id
WHERE rent.rent_date BETWEEN '2022-09-01' AND '2022-09-31'
GROUP BY cust.email
ORDER BY COUNT(DISTINCT inv.mov_id) DESC
FETCH FIRST 1 ROWS WITH TIES

SQL query to allow NULL for a highest_bid column when no bid has been placed yet

For school I need to make a function on an auction website. For this I need to join a couple of tables in a VIEW. This worked just fine, until I needed to add a filter for price range. Seems easy enough but the query result needs to allow a NULL when no bid has been placed.
The Statement for the View:
SELECT I.itemID, I.title, I.startPrice, B.highestBid, Cfi.category, I.endDate
FROM dbo.Items AS I INNER JOIN dbo.category_for_item AS Cfi ON V.itemID = Vir.itemID
LEFT OUTER JOIN dbo.Bid AS B ON V.itemID = B.itemID
This would get the following Table:
itemID title startPrice highestBid category endDate
1 1234 Alfa 25 26 PC 2018-09-22
2 1234 Alfa 25 NULL PC 2018-09-22
3 5678 Bravo 9 20 Console 2018-07-03
4 5678 Bravo 9 15 Console 2018-07-03
5 5678 Bravo 9 NULL Console 2018-07-03
6 9876 Charlie 84 100 Stamps 2018-06-14
7 9876 Charlie 84 90 Stamps 2018-06-14
8 9876 Charlie 84 85 Stamps 2018-06-14
9 9876 Charlie 84 NULL Stamps 2018-06-14
10 1470 Delta 98 100 Fashion 2018-06-15
11 1470 Delta 98 99 Fashion 2018-06-15
12 1470 Delta 98 NULL Fashion 2018-06-15
13 9631 Echo 56 65 Cars 2018-06-25
14 9631 Echo 56 NULL Cars 2018-06-25
15 7856 Foxtrot 98 NULL Dolls 2018-12-26
After looking around for answers I got a query for joining the VIEW on itself with only showing the highest bid instead of all bids:
SELECT VW.itemID, VW.title, VW.startPrice, VW.highestBid, VW.category, VW.endDate
FROM VW_SEARCH AS VW
INNER JOIN (SELECT itemID, MAX(highestBid) AS MaxBid
FROM VW_SEARCH
GROUP BY itemID) VJ
ON VW.itemID = VJ.itemID AND VW.highestBid = VJ.MaxBid
This gave the next results:
itemID title startPrice highestBid category endDate
1 1234 Alfa 25 26 PC 2018-09-22
2 5678 Bravo 9 20 Console 2018-07-03
3 9876 Charlie 84 85 Stamps 2018-06-14
4 1470 Delta 98 100 Fashion 2018-06-15
5 9631 Echo 56 65 Cars 2018-06-25
As I expected the result only showed the items with at least one bid on them. I tried added one extra condition on the subQuery and Joining RIGHT OUTER to make sure I would not get doubles of an itemID.
SELECT VW.itemID, VW.title, VW.startPrice, VW.highestBid, VW.category, VW.endDate
FROM VW_SEARCH AS VW
RIGHT OUTER JOIN (SELECT itemID, MAX(highestBid) AS MaxBid
FROM VW_SEARCH
WHERE highestBid > 0 OR highestBid IS NULL
GROUP BY itemID) VJ
ON VW.itemID = VJ.itemID AND VW.highestBid = VJ.MaxBid
This gave the following results (did not add result 5 - 1199 because it is all the same as result 4, this would happen in the actual table not the example table from above):
itemID title startPrice highestBid category endDate
1 1234 Alfa 25 26 PC 2018-09-22
2 5678 Bravo 9 20 Console 2018-07-03
3 9876 Charlie 84 85 Stamps 2018-06-14
4 NULL NULL NULL NULL NULL NULL
1200 1470 Delta 98 100 Fashion 2018-06-15
1201 9631 Echo 56 65 Cars 2018-06-25
While this is technicly allowing a NULL in the colums I need to get a result in the likes of :
itemID title startPrice highestBid catgory endDate
1 1234 Alfa 25 26 PC 2018-09-22
2 5678 Bravo 9 20 Console 2018-07-03
3 9876 Charlie 84 85 Stamps 2018-06-14
4 1470 Delta 98 100 Fashion 2018-06-15
5 9631 Echo 56 65 Cars 2018-06-25
6 7856 Foxtrot 98 NULL Dolls 2018-12-26
How do I get the desired result, or is it just impossible?
Also if the query could be written better, please say so.
Thanks in advance.
Solve the problem using a left join:
SELECT VW.itemID, VW.title, VW.startPrice, VW.highestBid, VW.category, VW.endDate
FROM VW_SEARCH VW LEFT JOIN
(SELECT itemID, MAX(highestBid) AS MaxBid
FROM VW_SEARCH
GROUP BY itemID
) VJ
ON VW.itemID = VJ.itemID AND VW.highestBid = VJ.MaxBid;
Or, use the ANSI-standard ROW_NUMBER() function:
select vw.*
from (select vw.*,
row_number() over (partition by itemID
order by highestBid nulls last
) as seqnum
from vw_search vw
) vw
where seqnum = 1;
This guarantees one row per item.
Note: Not all databases support NULLS LAST. This may not even be necessary, but you can also implement it using a case expression.
Can you give the definition of the view at least? Maybe the table definition too.
I would go only with subquery as because identity column :
select vw.*
from vw_search vw
where id = (select vm1.id
from vw_search vm1
where vm.itemID = vw1.itemID and vm1.highestBid is not null
order by vm1.highestBid desc
limit 1
);
However, some DBMS has not support LIMIT clause such as SQL Server if so, then you can use TOP clause instead.

Query with sum functions

for a little project(hobby purpose) i am building a C# application with a SQL database behind it.
However I am trying to build a query with a sum function which calculates values from a different table.
Here are the relevant tables and sample data
Hotel table
Id, Name Adress Zipcode Phone
1 Ankunding Group 90 Shelley Terrace 649-6326 86-(672)239-5855
2 Gerlach-Gutmann 50776 Bartillon Road 27109 CEDEX 33-(412)226-8055
3 Breitenberg-Smith 3289 Talisman Avenue 59762 86-(141)636-8780
4 Smitham-Marks 5 Veith Plaza 216282 7-(400)484-7233
5 Beatty LLC 3 Center Pass 940028 212-(310)974-4364
Reservation table
id, customerid, Startdate Enddate Amount of persons
1 163 2016-06-19 2017-04-30 4
2 172 2016-12-02 2016-08-18 5
3 162 2017-01-20 2017-04-08 3
4 66 2017-04-06 2017-01-07 2
5 104 2017-05-07 2016-09-10 2
RoomReservation table
Roomid, reservationid
3 53
3 198
4 178
5 172
5 218
Room table
id, hotelid, Roomnumber, price
1 1 1.01 268.83
2 1 1.02 201.28
3 1 1.03 126.64
4 1 1.04 122.56
5 1 1.05 217.41
Now I am trying to make a query to which gives me an overview off income per hotel. So for each hotel I want to get the reservations, and do amount of persons * the price of the room for each room in the hotel.
I've tried different things without success, I read somewhere that I needed to use a subquery but I have no idea how.
I want it to look like;
Hotelname1; income
Hotelname2; income
Hotelname3; income
Hotelname4; income
Hotelname4; income
Why can't you just do this:
SELECT
Hotel.Name,
SUM(Room.Price*Reservation.Amountofpersons)
FROM
Hotel
JOIN Room
ON Hotel.HotelId=Room.HotelId
JOIN RoomReservation
ON Room.RoomId=RoomReservation.RoomId
JOIN Reservation
ON RoomReservation.ReservationId=Reservation.ReservationId
GROUP BY
Hotel.Name
You can try it whit this query:
select hotel.name,sum(reservation.amount*room.price)
from hotel_table as hotel
inner join room_table as room on (hotel.hotelid=room.hotelid)
inner join roomreservation_table as room_reservation on (room.roomid=room_reservation.roomId)
inner join reservation_table as reservation on (room.reservationId=reservation.reservationid)
group by hotel.hotelid

Join on second table if value not found in first table

I would like to join on a second table only if the results of the first join are blank. Below is a subsection of Table A data:
ID Metro Submarket
1 NYC Manhattan
2 NYC Brooklyn
3 NYC Queens
4 NYC Bronx
5 NYC Newark
The tables I'm using for the joins are:
Table B Table C
Metro Submarket A.Price B.Price C.Price Metro A.Price B.Price C.Price
NYC Manhattan 54 32 48 NYC 50 49 69
NYC Queens 35 39 59 Philly 49 48 37
NYC Brooklyn 20 49 58 Chicago 20 48 36
NYC Bronx 49 30 20
NYC Newark 49 50 -
I'm adding the Price columns from Table B to Table A based on a Metro and Submarket match. However, Table B doesn't have all the prices. If I can't find a match in Table B then I want to look into Table C for a match only on Metro.
For ID 5, we can find the A and B prices in Table B. However, the C price is blank. In that case, I want it to retrieve the C price from Table C (69 is what it would choose).
I'm using SAS 9.4. SQL, macros, or anything else SAS can handle is welcome!
You can left join both tables to the main table and simply use COALESCE(). This will give you the value if present in Table B, otherwise it will give you the value in Table C:
PROC SQL;
CREATE TABLE Output AS
SELECT
ta.ID,
ta.Metro,
ta.Submarket,
COALESCE(tb.A_Price,tc.A_Price) AS A_Price,
COALESCE(tb.B_Price,tc.B_Price) AS B_Price,
COALESCE(tb.C_Price,tc.C_Price) AS C_Price
FROM
tablea ta
LEFT JOIN
tableb tb
ON (tb.Metro = ta.Metro)
AND (tb.Submarket = ta.Submarket)
LEFT JOIN
tablec tc
ON (tc.Metro = ta.Metro);
QUIT;

How to get sum of the same Unit's

I have a table which contains a college's departments and their units and sub-units.
OrganizationID ParentUnit Unit ChildUnit UnitName
10 1 0 0 Education
12 1 1 0 Sports
24 1 2 0 Mathmatics
28 1 3 0 Science
35 1 3 1 Physics
51 1 4 0 Arts
66 1 4 1 Music
69 1 4 2 Painting
84 8 0 0 Business & Administration
88 8 1 0 Administration
96 8 1 1 Public Administration
107 8 1 2 Local Managements
110 8 2 0 Finance
119 8 2 1 Accounting
124 8 2 2 Marketing
I have another table which contains the student information of that college.
StudentID OrganizationID
1 12
2 12
3 24
5 28
6 35
8 51
9 66
31 69
34 96
45 88
57 96
66 107
69 110
72 69
74 124
I want to get student counts for each unit. If a studutent's Organization is a ChildUnit it should be added to current Unit. If ChildUnit is greater than0 corresponding student count should be added to same Unit For example Physics is a child of Science. Then Science student count should return 2.
My target data table should look like as the following
ParentUnit UnitName StudentCount
------------------------------------------------------
Education Sports 2
Education Mathmatics 1
Education Science 2
Education Arts 4
Business & Administration Administration 4
Business & Administration Finance 2
I have done it in programmatic way. There are many for and if loops. Then I started to think whether it could be done with a smarter sql query.
That doesn't look so difficult. You are looking for the student count per ParentUnit + Unit. Then the name for such a group is the record where the level (the ChildUnit) is zero. You get that record with a CASE construct, then use MIN or MAX, because you need an aggregate function here (there should be exactly one record per group anyhow, so MIN = MAX).
select
min(case when o.childunit = 0 then o.unitname end) as unitname,
count(*) as studentcount
from organization o
inner join student s on s.organizationid = o.organizationid
group by o.parentunit, o.unit;
To include the parent unit name:
select
(
select unitname
from organization po
where po.parentunit = o.parentunit
and po.unit =0
and po.childunit = 0
) as parentunitname,
min(case when o.childunit = 0 then o.unitname end) as unitname,
count(*) as studentcount
from organization o
inner join student s on s.organizationid = o.organizationid
group by o.parentunit, o.unit;
Or:
select
min(po.unitname) as parentunitname,
min(case when o.childunit = 0 then o.unitname end) as unitname,
count(*) as studentcount
from organization o
inner join student s on s.organizationid = o.organizationid
inner join
(
select parentunit, unitname
from organization
where unit = 0 and childunit = 0
) po on po.parentunit = o.parentunit
group by o.parentunit, o.unit;