What happens to view table when a new row is inserted? - sql

Let's say I have 3 tables:
Products (BarCode[PK], PName, Price, QuantityInStock)
Saless (SaleID[PK], DeliveryAddress, CreditCard)
SaleItems (SaleID[PFK], BarCode[PFK], Quantity)
and then I create a view table called allproductsales:
create view allproductsales
as
select
s.saleid, p.barcode, p.pname
from
products p
left join
SALEITEMS si ON si.BARCODE = p.BARCODE
left join
saless s ON si.SALEID = s.SALEID;
and then I decide to insert new row into products like
INSERT INTO PRODUCTS
VALUES (3545322, 'Carrot', 0.10, 34);
and when I get display everything from allproductsales I can see newly inserted row there without its saleID. Is my query for creating a view wrong or it's how it should be?
Thanks.

A view is not a table. It does not hold any data: just a view to the actual tables. You can think of it as a stored query that you execute to get a result set whenever you query the view.
Keeping that in mind, what would happen when you execute the query the view is built with? since you are using left join, it probably means that you don't get any matching record on the sales table (or no record with the correct barcode on the salesItems table), therefor the salesId coloumn is null

The sales id is NULL because you have not inserted matching records into both sales tables (records for SaleItems and for Saless)
Once you insert sales data (matching records for SaleItems and for Saless) your join will have a value for Saless.SaleId as well.
Products | | SaleItems | | Saless
------------| join |------------------| join |---------
BarCode | | BarCode | SaleId | | SaleId
123 | | 123 | 456 | | 456
3545322 | | NULL | NULL | | NULL
Above you can see that you only get a result with all columns s.saleid, p.barcode, p.pname if the join can "match" records from table Products to SaleItems using the column BarCode and then match these records to Saless using the SaleId.
If there are no records in SaleItems (NULL) for the Barcode = 3545322 then the join can not match any records from Prodcuts to the other tables.

Is my query for creating a view wrong or it's how it should be?
That's how it should be. When you first insert a new row into Products there are no sales yet for that product, and so there is nothing to show for those fields. If you wanted to not see that product yet, you would define the view using an INNER JOIN instead of a LEFT JOIN.

Related

LEFT OUTER JOIN with 'field IS NULL' in WHERE works as INNER JOIN

Today I've faced some unexplainable (for me) behavior in PostgreSQL — LEFT OUTER JOIN does not return records for main table (with nulls for joined one fields) in case the joined table fields are used in WHERE expression.
To make it easier to grasp the case details, I'll provide an example. So, let's say we have 2 tables: item with some goods, and price, referring item, with prices for the goods in different years:
CREATE TABLE item(
id INTEGER PRIMARY KEY,
name VARCHAR(50)
);
CREATE TABLE price(
id INTEGER PRIMARY KEY,
item_id INTEGER NOT NULL,
year INTEGER NOT NULL,
value INTEGER NOT NULL,
CONSTRAINT goods_fk FOREIGN KEY (item_id) REFERENCES item(id)
);
The table item has 2 records (TV set and VCR items), and the table price has 3 records, a price for TV set in years 2000 and 2010, and a price for VCR for year 2000 only:
INSERT INTO item(id, name)
VALUES
(1, 'TV set'),
(2, 'VCR');
INSERT INTO price(id, item_id, year, value)
VALUES
(1, 1, 2000, 290),
(2, 1, 2010, 270),
(3, 2, 2000, 770);
-- no price of VCR for 2010
Now let's make a LEFT OUTER JOIN query, to get prices for all items for year 2010:
SELECT
i.*,
p.year,
p.value
FROM item i
LEFT OUTER JOIN price p ON i.id = p.item_id
WHERE p.year = 2010 OR p.year IS NULL;
For some reason, this query will return a results only for TV set, which has a price for this year. Record for VCR is absent in results:
id | name | year | value
----+--------+------+-------
1 | TV set | 2010 | 270
(1 row)
After some experimenting, I've found a way to make the query to return results I need (all records for item table, with nulls in the fields of joined table in case there are no mathing records for the year. It was achieved by moving year filtering into a JOIN condition:
SELECT
i.*,
p.year,
p.value
FROM item i
LEFT OUTER JOIN (
SELECT * FROM price
WHERE year = 2010 -- <= here I filter a year
) p ON i.id = p.item_id;
And now the result is:
id | name | year | value
----+--------+------+-------
1 | TV set | 2010 | 270
2 | VCR | |
(2 rows)
My main question is — why the first query (with year filtering in WHERE) does not work as expected, and turns instead into something like INNER JOIN?
I'm severely blocked by this issue on my current project, so I'll be thankful about tips/hints on the next related questions too:
Are there any other options to achieve the proper results?
... especially — easily translatable to Django's ORM queryset?
Upd: #astentx suggested to move filtering condition directly into JOIN (and it works too):
SELECT
i.*,
p.year,
p.value
FROM item i
LEFT OUTER JOIN price p
ON
i.id = p.item_id
AND p.year = 2010;
Though, the same as my first solution, I don't see how to express it in terms of Django ORM querysets. Are there any other suggestions?
The first query does not work as expected because expectation is wrong. It does not work as INNER JOIN as well. The query returns a record for VCR only if there is no price for VCR at all.
SELECT
i.*,
y.year,
p.value
FROM item i
CROSS JOIN (SELECT 2010 AS year) y -- here could be a table
LEFT OUTER JOIN price p
ON (p.item_id = i.id
AND p.year = y.year);

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.

Understanding Unnamed Column in SQL Select Statement Results

I am more familiar with a MongoDB architecture than sql. A colleague passed this query to me in order to get some data out of sybase:
SELECT order_items.contracted_rate FROM customers
JOIN orders
ON orders.customers_id = customer.id_number
JOIN order_items
ON order_items.order_id = orders.id,
ORDER by customer.id_number
When I run this query I get data that looks like this:
contracted_rate
1 | 20
2 | 14
3 | 18
My question is, what are the left-side numbers pertaining to from the above query? In other words, how can I map these item numbers (1, 2, 3) to a sybase table?
I'm used to seeing something more like this after running a sybase query:
id contracted_rate
1 | 20
2 | 14
3 | 18
So I'm trying to understand what the unnamed column refers to, and how I can map it to a sybase table to match up the correct customers?
Assuming this is the customerID you need, you can do this. The Id you show is not part of the result set
SELECT customer.id_number, order_items.contracted_rate
FROM customers
JOIN orders
ON orders.customers_id = customer.id_number
JOIN order_items
ON order_items.order_id = orders.id,
ORDER by customer.id_number

SQL - Join part of one table into another, based on two columns matching

I have two tables and I am looking to merge data from one table into another. Specifically, Table_A is millions of records of patient discharge's from Hospitals; table_B tells whether a particular hospital is labeled as an Acute Care Facility. I'm wanting to grab all records from table A where the Hospital is an Acute Care Facility.
Table A has many fields; of relevance:
HOSPITAL_ID | YEAR_AND_QUARTER | RECORD_ID
With record ID being unique. There are hundreds to thousands of record's (RECORD_ID's) per HOSTPIAL_ID, and hundreds of HOSPITAL_IDs per YEAR_AND_QUARTER
Table_B has a few fields:
HOSPITAL_ID | YEAR_ALONE | ACUTE_INDICATOR
1223 | 2004 | X
1223 | 2005 | X
1289 | 2004 |
1289 | 2005 | X
With Hospital_ID AND Year occuring only once together.
I can't join on Hospital_ID, because in Table B each Hospital ID occurs more than once. Also, table_B lumps all quarterly data into one year (instead of 2004Q1, 2004Q2.. only 2004).
Thus the final output (preferably as a new table) I want is just ACUTE_INDICATOR added to Table_A
HOSPITAL_ID | YEAR_AND_QUARTER | ACUTE_INDICATOR | RECORD_ID....
Appologies ahead, I'm an SQL infant and wasn't even quite sure what to search for an answer. My best guesses were (pseudo):
INNER JOIN (SELECT B.ACUTE_INDICATOR)
ON A.HOSPITAL_ID = B.HOSPITAL_ID
WHERE LEFT(A.YEAR_AND_QUARTER,4) = B.YEAR_ALONE
Many thanks :)
This will create the new table for you:
SELECT
a.HOSPITAL_ID,
a.YEAR_AND_QUARTER,
b.ACUTE_INDICATOR,
a.RECORD_ID
INTO c
FROM
a JOIN
b ON a.HOSPITAL_ID = b.HOSPITAL_ID
AND LEFT(a.YEAR_AND_QUARTER, 4) = b.YEAR_ALONE
Then if you wanted to query that table for Acute Care Facilities only...
SELECT * FROM c WHERE ACUTE_INDICATOR = 'x'
I would just use EXISTS for this:
<your select from table A>
FROM TableA A
WHERE EXISTS (SELECT 1
FROM TableB B
WHERE A.HOSPITAL_ID = B.HOSPITAL_ID
AND LEFT(A.YEAR_AND_QUARTER,4) = B.YEAR_ALONE
AND b.Acute-Indicator = 'X')
This won't give you any duplicate rows if there are 1000s per hospital in table B but still filters the way you want.

Which table exactly is the "left" table and "right" table in a JOIN statement (SQL)?

What makes a given table the left table?
Is it that the table is indicated in the "From" part of the query?
Or, is it the left table because it is on the left hand side of the = operator?
Are the following equivalent
SELECT *
FROM left_table
LEFT JOIN right_table ON left_table.right_id = right_table.id
and
SELECT *
FROM left_table
LEFT JOIN right_table on right_table.left_id = left_table.id
???
Thanks
The Left table is the first table in the select. Yes, your two examples are equivalent.
The right table is always the table that you are joining on. So yes, both of your statements are equivalent.
JOIN [Table] ON ...
[Table] is always the right table.
Roughly "left" is the result of everything that appears first in the whole FROM clause when reading from left to right - including the result of other JOINs, sub-queries, VIEWs and STORED PROCEDURES.
Both SQL statements are equivalent because the = operator at the ON part of the JOIN clause is symmetric (if a = b then b = a) so the result is the same no matter the order.
The regular join shows only the lines where the ON clause of the JOIN is true, while the LEFT JOIN shows also the records from "left" if the condition is false (showing NULL for any column from "right" present in the SELECT).
For example:
-- People: -- Car
id | name owner_id | model
---+------------ ---------+------------
1 | Paul 1 | Ferrari
2 | Nancy 2 | Porsche
3 | Arthur NULL | Lamborghini
4 | Alfred 10 | Maserati
> select people.name, car.model from people join car on car.owner_id=people.id;
name | model
---------+--------------
Paul | Ferrari
Nancy | Porsche
2 record(s) found
> select people.name, car.model from people left join car on
car.owner_id=people.id;
name | model
---------+--------------
Paul | Ferrari
Nancy | Porsche
Arthur | NULL
Alfred | NULL
4 record(s) found
> select people.name, car.model from people left join car on
people.id = car.owner_id;
name | model
---------+--------------
Paul | Ferrari
Nancy | Porsche
Arthur | NULL
Alfred | NULL
4 record(s) found
See this for a pretty good walkthrough on joins: http://en.wikipedia.org/wiki/Join_(SQL)
And yes, both statements are equivalent :-)
Yes, it's determined by the side of the JOIN operator the table appears on. Your two examples are indeed equivalent.
CREATE TABLE ORDERS (
ORDERID INT,
CUSTOMERID INT,
ORDERDATE DATE
);
INSERT INTO ORDERS VALUES (10123,10,DATE '16-08-20');
INSERT INTO ORDERS VALUES (10122,11,DATE '14-09-20');
INSERT INTO ORDERS VALUES (10121,12,DATE '10-10-20');
CREATE TABLE CUSTOMERS (
CUSTOMERID INT,
CUSTOMERNAME VARCHAR(20),
COUNTRY VARCHAR(20)
);
INSERT INTO CUSTOMERS VALUES (11 , 'BUDDHA','INDIA');
INSERT INTO CUSTOMERS VALUES (12 , 'JOHNWIK','UNITED STATES');
INSERT INTO CUSTOMERS VALUES (100, 'SERENA','UNITED KINGDOM');
discussing LEFT JOIN query:
select orders.orderid, customers.customername, orders.orderdate from orders
inner join customers on orders.customerid = customers.customerid;
If you want to know exact left and right tables. From left to right the table attached with from is [left] and table attached with join is [right].
Happy Hacking !!!