Assume a database with schema
TRUCK (license-plate, maker, model, year, mileage, max-load)
DRIVER (driver-id, name, age, address, license)
TRIP (license-plate, driver-id, date, origin, destination, miles, cargo, cost)
Write a SQL statement to list the names and driver-id of drivers who have taken trips to New York more times than they have taken trips to Washington.
I'm trying to express with something like
SELECT
name, driver-id
FROM
DRIVER, TRIP,
(SELECT name, driver-id
FROM DRIVER, TRIP
WHERE TRIP.driver-id = DRIVER.driver-id AND destination = “Washington”
GROUP BY name, driver-id) as TEMP
WHERE
TRIP.driver-id = DRIVER.driver-id
AND destination = “New York”
AND DRIVER.driver-id = TEMP.driver-id
GROUP BY
name, driver-id
HAVING
COUNT(*) > TEMP.COUNT(*);
Is this correct? Thanks!
I think you only need to aggregate by driver here and then assert that the count of trips to New York exceeds the count of trips to Washington:
SELECT
d.name,
d.driverid
FROM DRIVER d
INNER JOIN TRIP t
ON d.driverid = t.driverid
GROUP BY
d.name,
d.driverid
HAVING
SUM(t.destination = 'New York') >
SUM(t.destination = 'Washington');
This approach just makes a single pass over the joined tables using conditional aggregation for the two counts. I would not normally use your approach, because of the large subquery in the select clause.
use aggregation sum() and case keyword
select * from (
SELECT d.name, d.driver-id
, sum(case when destination = 'Washington' then 1 else 0 end) as washington
, sum(case when destination = 'New York' then 1 else 0 end) as newyork
FROM DRIVER d
INNER JOIN TRIP t ON t.driver-id = d.driver-id
GROUP BY d.name, d.driver-id) t1
where t1.newyork > t1.washington
Related
I have problem with querying data in MySQL, I want my data to display like this:
id
Name
Kp
price
KI014
TPD
SS10
1000
SS11
2000
SS12
3000
KI015
ASD
SK14
1500
SK15
2500
My query is like this:
SELECT SQL_CALC_FOUND_ROWS produkreseller.IDRESELLER as ID,
masterreseller.NAMARESELLER as Name,
produk.KodeProduk as KP,
produkreseller.hargajual as Price
FROM produkreseller
INNER JOIN produk ON produkreseller.IdProduk = produk.IDPRODUK
INNER JOIN masterreseller ON produkreseller.IDRESELLER = masterreseller.idreseller
ORDER BY masterreseller.idreseller;
And the result from that query is:
id
Name
Kp
price
KI014
TPD
SS10
1000
KI014
TPD
SS11
2000
KI014
TPD
SS12
3000
KI015
ASD
SK14
1500
KI015
ASD
SK15
2500
The basic idea is to partition the data by IDRESELLER, check if the current record is the first record in that partition, and if so, include the value of IDRESELLER (or NAMARESELLER) in the result, otherwise include the empty string.
SELECT CASE WHEN ROW_NUMBER() OVER (PARTITION BY produkreseller.IDRESELLER) = 1
THEN produkreseller.IDRESELLER
ELSE '' END as ID,
CASE WHEN ROW_NUMBER() OVER (PARTITION BY produkreseller.IDRESELLER) = 1
THEN masterreseller.NAMARESELLER
ELSE '' END as Name,
produk.KodeProduk as KP,
produkreseller.hargajual as Price
FROM produkreseller
INNER JOIN produk ON produkreseller.IdProduk = produk.IDPRODUK
INNER JOIN masterreseller ON produkreseller.IDRESELLER = masterreseller.idreseller
ORDER BY id;
This solution makes use of window function ROW_NUMBER(). More information about window functions can be found in the documentation.
Update
In MySQL 5 there are no window functions but one can make use of variables:
SELECT CASE WHEN ID <> #last_id THEN ID ELSE '' END as ID,
CASE WHEN ID <> #last_id THEN Name ELSE '' END as Name,
KP, Price,
#last_id := ID
FROM (SELECT produkreseller.IDRESELLER as ID,
masterreseller.NAMARESELLER as Name,
produk.KodeProduk as KP,
produkreseller.hargajul as Price
FROM produkreseller
INNER JOIN produk ON produkreseller.IdProduk = produk.IDPRODUK
INNER JOIN masterreseller ON produkreseller.IDRESELLER = masterreseller.idreseller
ORDER BY id) products, (SELECT #last_id := '') x;
As I don't have a possibility to run those queries, consider them as untested sketches (that is they may contain errors).
I'm currently trying to solve an issue revolving around summarizing a list of publishers, their total revenue, total payouts, and their net profit. What makes this slightly complicated is that the total payout is contingent on a case statement (due to having to choose between the higher value of royalties). This case statement was perfectly fine and executed in a previous query that you can see on the SQLFiddle link down below. My issue is that I have a near finished query that addresses what I need but I don't know what correction to make for it to complete. Help would be super appreciated! And if you get it to work, you would be a legit lifesaver!!
Select name,
SUM(book.msrp) AS 'Total Revenue',
SUM(EarningByBook) AS 'Total Payouts',
SUM(book.msrp)-SUM(EarningByBook) AS 'Net Profit'
FROM
(SELECT publisher.name, book.msrp,
(SELECT
CASE WHEN preferred_royalties > standard_royalties
THEN preferred_royalties*copies_sold
ELSE standard_royalties*copies_sold END
AS 'EarningByBook',
copies_sold ,
YEAR(CURDATE())-YEAR(date_published) Years
INNER JOIN book ON publisher.id = book.publisher_id)
FROM author A
JOIN book B ON A.id=B.author_id
JOIN publisher P ON B.publisher_id=P.id)
From publisher
INNER JOIN book ON publisher.id = book.publisher_id) Z
GROUP BY
name;
The SQL fiddle is as follows :
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=b0015a0a4286f9b2c064bbd65525faa5&hide=13312
The output expected should look
Publisher
Total Revenue
Total Payouts
Net Profit
name
20000
1500
18500
name
15000
1000
14000
Consider flattening all your inner selects to a single SELECT subquery.
SELECT sub.publisher
, SUM(sub.msrp) AS 'Total Revenue'
, SUM(sub.EarningByBook) AS 'Total Payouts'
, SUM(sub.msrp) - SUM(sub.EarningByBook) AS 'Net Profit'
FROM
(SELECT
P.`name` AS publisher
, CASE
WHEN A.preferred_royalties > P.standard_royalties
THEN A.preferred_royalties * B.copies_sold
ELSE P.standard_royalties * B.copies_sold
END AS EarningByBook
, YEAR(CURDATE()) - YEAR(B.date_published) AS Years
, B.msrp
, B.copies_sold
FROM author A
INNER JOIN book B
ON A.id = B.author_id
INNER JOIN publisher P
ON B.publisher_id = P.id
) AS sub
GROUP BY
sub.publisher;
I'm trying to take all the class tables that have more students that are male than female and list them
I've tried to do this a way that i'll show in the code
SELECT S.SID
FROM CLASS C, STUDENT S,
(SELECT COUNT(*)
FROM CLASS C, STUDENT S
WHERE S.GENDER = 'M') AS S(M),
(SELECT COUNT(*)
FROM CLASS C, STUDENT S
WHERE S.GENDER = 'F') AS S1(F)
WHERE S.ClassNo = C.ClassNumber AND S(M) > S1(F)
I've tried it a few other ways including putting the select count statements in the 'where' place. I can put the SELECT count statements separately outside of the big statement and it will show the correct values but for comparison, it is not working so I assume it has to do with how they're being compared. I expect the output to show all the classes that have more males than females.
Your query is a bit all over the place. Assuming what you want is to get the class numbers where males>females:
select * from (
select c.ClassNumber,
sum(case when S.GENDER = 'M' then 1 else 0 end) as m,
sum(case when S.GENDER = 'F' then 1 else 0 end) as f
from class c
join student s on s.ClassNo = C.ClassNumber
group by c.ClassNumber
) q
where m>f
Simply use aggregation:
SELECT S.CLASSNO
FROM STUDENT S
GROUP BY S.CLASSNO
HAVING SUM(GENDER = 'M') > SUM(GENDER = 'F');
Note that there is no need to use the CLASS table, because the class number is in the STUDENT table.
I'm writing a SQL Statement to get some values in a Recordset, which I'll use to transfer the result into TextBoxes on a Form in Excel. The tables involved are:
Customers -> CustomerId, FirstName, LastName, TelNumber
Invoice -> InvoiceId, CustomerId, CarModel, CarColor, CarPlate
Repairs -> RepairId, InvoiceId, TypeOfRepair, PartOfCar, Price
Services -> ServiceId, InvoiceId, Date, Status
When a Customer comes to the Garage, an Invoice is created, which is associated with this customer. An invoice can have many Repairs. The customer goes away without repairing the car, but the invoice is there. If the customer decides to repair the car, then a Service is created, which starts with the Status "working on it...". When the service is done, the status change to "Waiting for Check Out..."
I want to use a SQL Statement to retrieve the following Values (columns) for a specific InvoiceId:
CarModel, Color, Plate, CustomerName (FirstName LastName), PaintingTotalValue (where 'Painting' is one type in the column 'Type'), OtherTotalValue (total price of all the other types of repairs in this invoice), total price (total price, that is, painting + other ones).
I wrote the following, to get the values, but I don't know how to get the PaintingTotalValue and OtherTotalVAlue.
SELECT i.CarModel, i.Color, i.Plate, CONCAT(c.FirstName,' ',c.LastName) AS Name, FORMAT(SUM(r.Price),2) AS TotalPrice
FROM Services AS s INNER JOIN Invoices AS i ON s.invoiceId=i.invoiceId
INNER JOIN Repairs AS r ON s.invoiceId=r.invoiceId
INNER JOIN Customers AS c ON i.customerId=c.customerId
WHERE s.invoiceId = 15
Use CASE WHEN in your SELECT clause, to select the value that's conditional to the type:
SELECT
...
CASE WHEN r.Type = 'Painting' THEN r.Price ELSE 0 END PaintWorkPrice,
CASE WHEN r.Type <> 'Painting' THEN r.Price ELSE 0 END OtherWorkPrice,
FROM ...
That's one thing.
The other thing is that you're not selecting anything from the Services table, and making your query much more complicated than it needs to be.
If you can modify the schema, remove the ServiceId primary key field, and use Services.InvoiceId as a primary key instead: that will enforce the 1:1 relationship naturally.
FROM Repairs r
INNER JOIN Invoices i ON r.InvoiceId = i.InvoiceId
INNER JOIN Customers c ON i.CustomerId = c.CustomerId
The data you want to aggregate is granular to Repairs, so you select FROM that, and then move your way through the foreign keys up to Customers.
SELECT
i.CarModel
,i.Color
,i.Plate
,CONCAT(c.FirstName,' ',c.LastName) Name
,CASE WHEN r.Type = 'Painting' THEN r.Price ELSE 0 END PaintWorkPrice
,CASE WHEN r.Type <> 'Painting' THEN r.Price ELSE 0 END OtherWorkPrice
,r.Price
FROM Repairs r
INNER JOIN Invoices i ON r.InvoiceId = i.InvoiceId
INNER JOIN Customers c ON i.CustomerId = c.CustomerId
That's not aggregated yet: there's a record for each repair, for every invoice, under every customer that has an invoice. That part is the sub-query. If you have a parameter, that's where you use it.
WHERE i.InvoiceId = pInvoiceId
If you're just hard-coding an ID, that's where you do it too.
Now type SELECT q.* FROM ( on the line above, and ) q under the WHERE clause, then replace the q.* with the fields you're not aggregating - and aggregate the others. The result should be something like this:
SELECT
q.CarModel
,q.Color
,q.Plate
,q.Name
,SUM(q.PaintWorkPrice) PaintAmount
,SUM(q.OtherWorkPrice) OtherAmount
,SUM(q.Price) TotalAmount
FROM (
SELECT
i.CarModel
,i.Color
,i.Plate
,CONCAT(c.FirstName,' ',c.LastName) Name
,CASE WHEN r.Type = 'Painting' THEN r.Price ELSE 0 END PaintWorkPrice
,CASE WHEN r.Type <> 'Painting' THEN r.Price ELSE 0 END OtherWorkPrice
,r.Price
FROM Repairs r
INNER JOIN Invoices i ON r.InvoiceId = i.InvoiceId
INNER JOIN Customers c ON i.CustomerId = c.CustomerId
WHERE i.InvoiceId = 15
) q
GROUP BY
q.CarModel
,q.Color
,q.Plate
,q.Name
I'm having trouble creating a query that will report on training completions by users that are grouped by district within a region. For one training course the report needs to show for each district
Number of users assigned course
Number of users completed course
Percentage of users completed course
The output report should look like this (without the periods).:
District.........Assigned.......Completed....%
Arkansas..........20..............15...............75%
Illinois...............80..............80...............100%
Iowa.................10...............8.................80%
Michigan..........30..............20................66%
Here's the SQL query I have tried using
Select mytable.district as District,
(Select Count(Distinct mytable.user_id)
From mytable
Where mytable.district = District AND
mytable.training_title = 'My Course') As 'Assigned',
(Select Count(Distinct mytable.user_id)
From mytable
Where mytable.training_status = "Completed" AND
mytable.district = District AND
mytable.training_title = 'My Course') as 'Completed',
Concat(Round(100 * (Select Count(Distinct mytable.user_id)
From mytable
Where mytable.training_status = "Completed" AND
mytable.district = District AND
mytable.training_title = 'My Course') / (Select Count(Distinct mytable.user_id)
From mytable
Where
mytable.district = District AND
mytable.training_title = 'My Course'),0 ),"%") as '%'
From mytable
Where mytable.region = 'Midwest'
Group by District
It doesn't work at all like this. However if I substitute one of the district values (such as Arkansas) in for 'District' in the WHERE clause I can get the correct values for that district. However i need to find all of the districts for each region and calculate the values for district. This is only one of the regions I need to create the query for.
Two key issues:
All of the data exists in one table in my database. I have consolidated and imported it from another database.
The data contains duplicates Therefore I must use Distinct to eliminate the duplicates from the results.
Suggestions? Thanks in advance for your assistance!!!
As far as I can see, you could just sum things up as you want them and GROUP BY district. The subquery will take care of the duplicate rows.
SELECT
district,
COUNT(*) Assigned,
COUNT(CASE WHEN training_status='Completed' THEN 1 END) Completed,
CONCAT(COUNT(CASE WHEN training_status='Completed' THEN 1 END) /
COUNT(*) * 100, '%') `%`
FROM (
SELECT DISTINCT * FROM mytable
WHERE mytable.region = 'Midwest'
AND mytable.training_title = 'My Course') z
GROUP BY district;
An SQLfiddle to test with.