SQL Joins with 4 tables - sql

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.

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.

How to fix sql query problem with two or more position which have one ID

I have sql query where I have to join three tables. One of this is a table with data of invoice, it looks like this:
INVOICE
ID CUSTOMER_NAME TAXID NUMBER LABEL GUID
1 CUSTOMER1 8739281100 FV001/2019 1 04EABFB3-0B9D-4749-B99D-A4EBEE079633
POSITION OF INVOICE
ID ID_INV POSITION_NAME COUNT
1 1 NAME1 3
2 1 NAME2 2,5
TABLE WITH LABEL
ID NAME VALUE GUID_INV
1 LABEL1 true 04EABFB3-0B9D-4749-B99D-A4EBEE079633
When I want to run this query I have statement like this multiple rows in singleton select.
This is for Firebird 2.5.
SELECT
a.ID,
a.GUID,
a.NUMBER,
a.CUSTOMER_NAME,
b.COUNT,
(select usrd.LABEL from USER_FIELD_DEFS usrd
where usrd.GUID_INV=a.GUID and (usrd.ID=1 and usrb.VALUE='true')) as LABEL_NAME
FROM INVOICE a
join POSITION_INVOICE b ON a.ID=b.ID_INV
I want to get result like this
1 04EABFB3-0B9D-4749-B99D-A4EBEE079633 FV001/2019 CUSTOMER1 3 LABEL1
1 04EABFB3-0B9D-4749-B99D-A4EBEE079633 FV001/2019 CUSTOMER1 2,5 LABEL1
Please help with this. I know that solution maybe is very simple but I have some eclipse of the mind:)
This should give you the rows you want based on the 3 tables you provided. If there is a chance that an invoice has no position then simply replace the inner join with left join
SELECT
I.[Id]
,I.[GUID]
,I.[NUMBER]
,I.[CUSTOMER_NAME]
,IP.[POSITION_NAME]
,L.[NAME]
FROM [INVOICE] I
INNER JOIN [IN_P] IP ON IP.ID_INV = I.Id
LEFT JOIN [LABEL] L ON L.[GUID_INV] = I.[GUID]
You are just missing one more join here. Assuming USER_FIELD_DEFS is the same as TABLE WITH LABEL that you have mentioned here
SELECT
a.ID,
a.GUID,
a.NUMBER,
a.CUSTOMER_NAME,
b.COUNT,
c.NAME
FROM INVOICE a
JOIN POSITION_INVOICE b ON a.ID=b.ID_INV
JOIN USER_FIELD_DEFS c ON c.GUID_INV = a.GUID AND c.ID=1 and c.VALUE='true'

SQL - Return all records if column value matches pattern?

I have a query below that returns a very large set of data:
SELECT
a.id,
a.name,
c.c_name,
l.t_id,
t.n_id,
l.lr_id,
l.created,
l.am_id,
e.e_name
FROM l
INNER JOIN e
ON l.e_id=e.e_id
INNER JOIN t
ON l.t_id=t.t_id
INNER JOIN a
ON l.a_id=a.a_id
INNER JOIN c
ON a.p_c_id=c.c_id
WHERE lower(c.c_name) LIKE 'XXX' and l.date_created > 'YYYY-MM-DD'
ORDER BY a_name, created
The data is useful, but it's such a large dataset that it makes it very difficult to find the specific issues I'm searching for.
I'd like to be able to re-write the where clause to search for a string pattern in the e.e_name column. (WHERE e.e_name like XXXX) Then return ALL records for any a.id that has ANY e.e_name record that matches the search string.
I just can't figure out how to return all records for an a.id if it has an e.e_name match. :(
EDIT: So the simplified data would look something like
555 | Bob Jones | Staff | 123 | 232 | 454 | 2015-02-27 1:00 | Location 1
556 | Bob Jones | Staff | 123 | 232 | 456 | 2015-02-27 1:01 | Location 2
Essentially repeating for Bob Jones with many locations. So what I'm trying to do is find ALL the records for Bob Jones (or any other person) who has ANY record with a Location that matches a search string. My problem is that doing a LIKE or = search returns ONLY the records that match, and not all the surrounding records (which I need for context).
Are you looking for this?
SELECT
a.id,
a.name,
c.c_name,
l.t_id,
t.n_id,
l.lr_id,
l.created,
l.am_id,
e.e_name
FROM l
INNER JOIN e
ON l.e_id=e.e_id
INNER JOIN t
ON l.t_id=t.t_id
INNER JOIN a
ON l.a_id=a.a_id
INNER JOIN c
ON a.p_c_id=c.c_id
WHERE a.name IN (
SELECT a1.name
FROM (All the same joins)
WHERE e.e_name LIKE '%XXX%'
)
AND l.date_created > 'YYYY-MM-DD'
ORDER BY a_name, created
I would, first of all, find all matches for your c.c_name and put them into temporary table, maybe add nonclustered index on it, then only query it
So that would be my approach:
SELECT c.c_id, c.c_name
INTO #TempC
FROM c
WHERE c.c_name LIKE 'XXXX'
CREATE NONCLUSTERED INDEX idx_id ON #Temp (c_id)
SELECT
a.id,
a.name,
c.c_name,
l.t_id,
t.n_id,
l.lr_id,
l.created,
l.am_id,
e.e_name
FROM l
INNER JOIN e
ON l.e_id=e.e_id
INNER JOIN t
ON l.t_id=t.t_id
INNER JOIN a
ON l.a_id=a.a_id
INNER JOIN #TempC AS C
ON a.p_c_id=c.c_id
WHERE l.date_created > 'YYYY-MM-DD'
ORDER BY a_name, created
Maybe that would boost performance by a bit.
Also, is your query using indexes correct?

SQL query to extract based on two conditions

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

Suggest optimized query using 4 tables ( RIGHT JOIN v/s INNER JOIN & HAVING )

I have following table structure
table_country ==> country_id (PK) | country | status
table_department ==> department_id (PK) | department | country_id (FK)
table_province ==> province_id (PK) | province | department_id (FK)
table_district ==> district_id (PK) | district | province_id (FK)
NOTE: all tables engine are innoDB
One country can have multiple department, one department can have multiple province and one province can have multiple district. Now I need to search only those country which have at least one district
I have written the below 2 SQL queries, in my case, both queries return the same results.... please describe the difference between those queries
Using a RIGHT JOIN:
SELECT c.country_id as id, c.country as name
FROM table_country c
RIGHT JOIN table_department d ON d.country_id=c.country_id
RIGHT JOIN table_province p ON p.department_id=d.department_id
RIGHT JOIN table_district ds ON ds.province_id=p.province_id
WHERE c.status='Active' GROUP BY (c.country_id)
Using INNER JOIN and HAVING clause:
SELECT COUNT(ds.district), c.country_id as id, c.country as name
FROM table_country c
INNER JOIN table_department d ON d.country_id = c.country_id
INNER JOIN table_province p ON p.department_id = d.department_id
INNER JOIN table_district ds ON ds.province_id = p.province_id
WHERE c.status='Active'
GROUP BY (c.country_id)
HAVING COUNT(ds.district)>0
Please tell me where these both query make the difference in results and which one I have to use or do I have to use a different query?
Thanks in advance
I would suggest using the second of your 2 queries (with inner joins), but without the HAVING clause as it isn't required because the inner joins require that any row in the final result MUST have a row in the district table.
The first of your queries, using a more exotic series of RIGHT OUTER JOINS, ultimately produces the same outcome - but because they are outer joins it is potentially less efficient. Another way of representing your first query would be to reverse the table sequence like this:
SELECT
c.country_id AS id
, c.country AS name
, COUNT(ds.district)
FROM table_district ds
INNER JOIN table_province p ON ds.province_id = p.province_id
INNER JOIN table_department d ON p.department_id = d.department_id
INNER JOIN table_country c ON d.country_id = c.country_id
WHERE c.status='Active'
GROUP BY
c.country_id
, c.country
and hopefully when inverted like that it is clear that no result row can exist unless there is a row from the district table.