SQL query to extract based on two conditions - sql

I have four tables in this structure
cars - id | brand | type | license
equipments - id | name | description
distances - id_car | date | distance
cars_equipments - id_car | id_equipment
and i have to extract all cars that have equipment "fire extinguisher" that were driving yesterday...
I have managed to create this so far
SELECT cars.id, cars.brand, cars.type, cars.license, cars_equipments.id_car
FROM equipments
INNER JOIN cars_equipments ON equipments.id = cars_equipments.id_equipment
INNER JOIN cars ON cars.id = cars_equipments.id_car
INNER JOIN distances ON distances.id_car = cars.id
WHERE equipments.name LIKE 'fire extinguisher'
this returns only one car having 'fire extinguisher' so it's not working properly, and if I add
AND distances.date = DATE_SUB(NOW(), INTERVAL 1 DAY)
to combine that with the last day it doesn't work.

Does it work for you?
SELECT cars.id, cars.brand, cars.type, cars.license, cars_equipments.id_car
FROM cars
INNER JOIN cars_equipments ON cars.id = cars_equipments.id_car
INNER JOIN equipments ON equipments.id = cars_equipments.id_equipment
INNER JOIN distances ON distances.id_car = cars.id
WHERE equipments.name LIKE 'fire extinguisher'
AND distances.date >= dateadd(day,datediff(day,0,GETDATE()),-1)
AND distances.date < dateadd(day,datediff(day,0,GETDATE()),0)
Check this sql fiddle

Related

Postgres join and count multiple relational tables

I want to join the 2 tables to the first table and group by a vendor name. I have three tables listed below.
Vendors Table
| id | name
|:-----------|------------:|
| test-id | Vendor Name |
VendorOrders Table
| id | VendorId | Details | isActive(Boolean)| price |
|:-----------|------------:|:------------:| -----------------| --------
| random-id | test-id | Sample test | TRUE | 5000
OrdersIssues Table
| id | VendorOrderId| Details. |
|:-----------|--------------:-----------:|
| order-id | random-id | Sample test|
The expected output is to count how many orders belong to a vendor and how many issues belongs to a vendor order.
I have the below code but it's not giving the right output.
SELECT "vendors"."name" as "vendorName",
COUNT("vendorOrders".id) as allOrders,
COUNT("orderIssues".id) as allIssues
FROM "vendors"
LEFT OUTER JOIN "vendorOrders" ON "vendors".id = "vendorOrders"."vendorId"
LEFT OUTER JOIN "orderIssues" ON "orderIssues"."vendorOrderId" = "vendorOrders"."id"
GROUP BY "vendors".id;```
You need the keyword DISTINCT, at least for allOrders:
SELECT v.name vendorName,
COUNT(DISTINCT vo.id) allOrders,
COUNT(DISTINCT oi.id) allIssues
FROM vendors v
LEFT OUTER JOIN vendorOrders vo ON v.id = vo.vendorId
LEFT OUTER JOIN orderIssues oi ON oi.vendorOrderId = vo.id
GROUP BY v.id, v.name;
Consider using aliases instead of full table names to make the code shorter and more readable.
You are joining along two related dimensions. The overall number of rows is the number of issues. But to get the number of orders, you need a distinct count:
SELECT v.*, count(distinct vo.id) as num_orders,
COUNT(oi.vendororderid) as num_issues
FROM vendors v LEFT JOIN
vendorOrders vo
ON v.id = vo.vendorId LEFT JOIN
orderIssues oi
ON oi.vendorOrderId = vo.id
GROUP BY v.id;
Notes:
Table aliases make the query easier to write and to read.
Quoting column and table names makes the query harder to write and read. Don't quote identifiers (you may need to recreate the tables).
Postgres support SELECT v.* . . . GROUP BY v.id assuming that the id is the primary key (actually, it only needs to be unique). This seems like a reasonable assumption.

SQL query to retrieve average of different aggregation type in single query

This is my data model:
I need to get name, surname and personal votes average of all students in classroom that have the average lower then the votes average of all students in classroom. Following the desired output:
+---------+------+--------+-------------+---------------+
| surname | name | class | class_avg | studend_avg |
+---------+------+--------+-------------+---------------+
| b | b | 1 | 4.1250 | 2.7500 |
+---------+------+--------+-------------+---------------+
I wrote following query that works correctly in mariadb database:
SELECT student.surname, student.name, student.classroom, classroom.average AS classroom_average, AVG(vote.vote) AS student_average
FROM (student INNER JOIN vote ON student.fiscalcode = vote.fiscalcode)
INNER JOIN
(select student.classroom AS classroom, AVG(vote.vote) AS average
FROM student INNER JOIN vote ON student.fiscalcode = vote.fiscalcode
GROUP BY student.classroom) AS classroom
ON student.classroom=classroom.classroom
GROUP BY student.surname, student.name, student.classroom, classroom.average
HAVING AVG(vote.vote) < classroom.average;
But I have this error in MS Access:
Your Query does not include the specified expression "AVG(vote.vote)/AVG(vote.vote) < classroom.average" as part of an aggregate function
There are some other easier method to write this query?
Consider:
SELECT StudentAvg.fiscalcode, student.name, student.surname, StudentAvg.AvgOfvote, ClassAvg.AvgOfvote
FROM ((SELECT student.classroom, student.fiscalcode, Avg(vote.vote) AS AvgOfvote
FROM student INNER JOIN vote ON student.fiscalcode = vote.fiscalcode
GROUP BY student.classroom, student.fiscalcode) As StudentAvg INNER JOIN (SELECT student.classroom, Avg(vote.vote) AS AvgOfvote
FROM vote INNER JOIN student ON vote.fiscalcode = student.fiscalcode
GROUP BY student.classroom) AS ClassAvg ON StudentAvg.classroom = ClassAvg.classroom) INNER JOIN student ON StudentAvg.fiscalcode = student.fiscalcode
WHERE (((StudentAvg.AvgOfvote)<[ClassAvg]![AvgOfvote]));

PL/SQL Oracle Sql - Filter query results by max of column/attribute

i haven't really touched much of PL/SQL before this project and my current knowledge of DBs is limited to SQLite, MySQL, PostgreSQL and other non sql DB technologies so bear with me on this.
First i started with a query that would get me the max difference in hours between two dates for each ID i had on that table. It goes something like this:
SELECT
id_trip as ID,
MAX(24 * (Trip.actual_arrival_date- (Trip.programmed_arrival_date))) as MAX_DELAY_HOURS
FROM Trip
GROUP BY ID
And that returns me something like this:
Results of first query
So i can say that i successfully went into the all the trips that exist for each ID and got the ones that have the maximum delay.
Now what i want to do after that is to join other types of information to that table, namely the actual date of the trip, the name of the departure spot and the name of the arrival spot. So i did something like this:
SELECT
internal_ID as external_ID,
programmed_date,
starting_airport.name as starting_airport,
destination_airport.name as destination_airport,
24 * (Trip.actual_arrival_date- (Trip.programmed_arrival_date)) AS external_delay
FROM Regular_flight
INNER JOIN Trip ON Regular_flight.ID = Trip.ID_Regular_flight
INNER JOIN Flight ON Regular_flight.ID_Flight = Flight.ID
INNER JOIN Airport starting_airport ON starting_airport.ID_IATA = Flight.ID_STARTING_AIRPORT
INNER JOIN Airport destination_airport ON destination_airport.ID_IATA = Voo.ID_DESTINATION_AIRPORT
INNER JOIN (
--this is the query that i built before--
SELECT
id_trip as internal_ID,
MAX(24 * (Trip.actual_arrival_date- (Trip.programmed_arrival_date))) as MAX_DELAY_HOURS
FROM Trip
GROUP BY ID
) ON internal_ID = external_ID
Order by external_ID;
And this actually returns something like this:
Results of second query
Now my problem is that while i have all the info i need there... i wanted to filter it out so that it only shows me the highest EXTERNAL_DELAY for each EXTERNAL_ID.
Usually i'd do a GROUP BY EXTERNAL_ID but since i'm selecting many things not just EXTERNAL_ID it won't actually execute the code. I've tried to do a GROUP BY with all the columns i'm selecting in the external query but then i have all the "combinations" between the external_ID and Programmed_date which is not what i'm looking for.
Basically from the 2nd query i want to reach something like this:
| EXTERNAL_ID | PROGRAMMED_DATE | STARTING_AIRPORT | DESTINATION_AIRPORT | EXTERNAL_DELAY |
| 1 | 16.07.08 | Aeroporto de Gatwick | Aeroporto Francisco Sa Carneiro | 744 |
| 2 | 16.08.08 | Aeroporto de Gatwick | Aeroporto Francisco Sa Carneiro | 0 |
| 3 | 16.08.09 | Aeroporto Francisco Sa Carneiro | Aeroporto Francisco Sa Carneiro | 744 |
And so on for each ID, so basically for each ID the the MAXIMUM delay found no matter what the date is.
I've been scratching my head for a few hours now and i'd like to have someone point me out in the right direction.
Appreciate any help i can get.
SELECT *
FROM
(SELECT x.*,
ROW_NUMBER() OVER (PARTITION BY external_ID ORDER BY external_delay DESC NULLS LAST) r
FROM (
SELECT internal_ID AS external_ID,
programmed_date,
starting_airport.name AS starting_airport,
destination_airport.name AS destination_airport,
24 * (Trip.actual_arrival_date- (Trip.programmed_arrival_date)) AS external_delay
FROM Regular_flight
INNER JOIN Trip
ON Regular_flight.ID = Trip.ID_Regular_flight
INNER JOIN Flight
ON Regular_flight.ID_Flight = Flight.ID
INNER JOIN Airport starting_airport
ON starting_airport.ID_IATA = Flight.ID_STARTING_AIRPORT
INNER JOIN Airport destination_airport
ON destination_airport.ID_IATA = Voo.ID_DESTINATION_AIRPORT
INNER JOIN
(
--this is the query that i built before--
SELECT id_trip AS internal_ID,
MAX(24 * (Trip.actual_arrival_date- (Trip.programmed_arrival_date))) AS MAX_DELAY_HOURS
FROM Trip
GROUP BY ID
)
ON internal_ID = external_ID
) x) WHERE r =1
Order by external_ID;

SQL Joins with 4 tables

I have a problem here and I got a bit confused with outer/inner joins and multiple conditions
We have 4 tables with - columns:
table_cars - id | brand | type | license
table_equipments - id | name | description
table_distances - id_car | date | distance
table_cars_equipments - id_car | id_equipment
First query should show all cars that have equipment "fire extinguisher" and have been driving yesterday.
I have tried to write this query:
SELECT table_cars_equipments.id_car
FROM table_equipments
INNER JOIN table_cars_equipments
ON table_equipments.id = table_cars_equipments.id_equipment
AND table_equipments.name LIKE 'fire extinguisher';
Though I am still confused how to add the cars which had been driving yesterday, I don't know how to make the connection with the table table_distances.
Add another JOIN with the table table_cars and another one to the table table_distances. Then add a condition to the WHERE clause to get only those cars that have been driving yesterday. Something like this:
SELECT
c.id,
c.brand,
c.type,
c.license
ce.id_car,
...
from table_equipments AS e
INNER JOIN table_cars_equipments AS ce ON e.id = ec.id_equipment
INNER JOIN table_cars AS c ON c.id = ce.id_car
INNER JOIN table_distances AS d ON d.id_car = c.id
WHERE e.name LIKE 'fire extinguisher'
AND d.date = ?;
Note that: I used aliases for the table c, e etc, instead of the tables' full names.

How to find all the products with specific multi attribute values

I am using postgresql.
I have a table called custom_field_answers. The data looks like this
Id | product_id | value | number_value |
4 | 2 | | 117 |
3 | 1 | | 107 |
2 | 1 | bangle | |
1 | 2 | necklace | |
I want to find all the products which has text_value as 'bangle' and number_value less than 50.
Here was my first attempt.
SELECT "products".* FROM "products" INNER JOIN "custom_field_answers"
ON "custom_field_answers"."product_id" = "products"."id"
WHERE ("custom_field_answers"."value" ILIKE 'bangle')
Here is my second attempt.
SELECT "products".* FROM "products" INNER JOIN "custom_field_answers"
ON "custom_field_answers"."product_id" = "products"."id"
where ("custom_field_answers"."number_value" < 50)
Here is my final attempt.
SELECT "products".* FROM "products" INNER JOIN "custom_field_answers"
ON "custom_field_answers"."product_id" = "products"."id"
WHERE ("custom_field_answers"."value" ILIKE 'bangle')
AND ("custom_field_answers"."number_value" < 50)
but this does not select any product record.
A WHERE clause can only look at columns from one row at a time.
So if you need a condition that applies to two different rows from a table, you need to join to that table twice, so you can get columns from both rows.
SELECT p.*
FROM "products" AS p
INNER JOIN "custom_field_answers" AS a1 ON p."id" = a1."product_id"
INNER JOIN "custom_field_answers" AS a2 ON p."id" = a1."product_id"
WHERE a1."value" = 'bangle' AND a2."number_value" < 50
It produces no records because there is no custom_field_answers record that meets both criteria. What you want is a list of product_ids that have the necessary records in the table. Just in case no one gets to writing the SQL for you, and until I have a chance to work it out myself, I thought I would at least explain to you why your query is not working.
This should work:
SELECT p.* FROM products LEFT JOIN custom_field_answers c
ON (c.product_id = p.id AND c.value LIKE '%bangle%' AND c.number_value
Hope it helps
Your bangle-related number_value fields are null, so you won't be able to do a straight comparison in those cases. Instead, convert your nulls to 0s first.
SELECT "products".* FROM "products" INNER JOIN "custom_field_answers"
ON "custom_field_answers"."product_id" = "products"."id"
WHERE ("custom_field_answers"."value" LIKE '%bangle%')
AND (coalesce("custom_field_answers"."number_value", 0) < 50)
Didn't actually test it, but this general idea should work:
SELECT *
FROM products
WHERE
EXISTS (
SELECT *
FROM custom_field_answers
WHERE
custom_field_answers.product_id = products.id
AND value = 'bangle'
)
AND EXISTS (
SELECT *
FROM custom_field_answers
WHERE
custom_field_answers.product_id = products.id
AND number_value < 5
)
In plain English: Get all products such that...
there is a related row in custom_field_answers where value = 'bangle'
and there is (possibly different) related row in custom_field_answers where number_value < 5.