SQL join tables with placing null to not existing values - sql

I'm trying to find which market doesn't have which product according to visit date.
In order to do this I thought if I give all products to all markets there will be null dates because there will be no visit for that product for the market. To see
all products in the market I wrote a query :
SELECT id, p.ProductName FROM atb_markets
CROSS JOIN
(SELECT StokAd FROM atb_products) p
and I got this kind of a view:
MarketId productName
1 a
1 b
1 c
1 d
1 e
1 f
2 a
2 b
2 c
2 d
2 e
. .
. .
By the way these all are different tables (atb_markets, atb_products) and dates are saving on the outformmobiledata table. This table holds each record for the markets. marketId, productId and date is holding on this table (date information comes from mobile devices). Because of I need productName I need atb_products table also.
Finally I need this view by using these three tables. If I get null values I can know which products don't exists on a market.
And this is the view I need:
date MarketId productName
01.12.2013 1 a
11.12.2013 1 b
NULL 1 c
04.12.2013 1 d
20.12.2013 1 e
05.12.2013 1 f
06.12.2013 2 a
NULL 2 b
NULL 2 c
12.12.2013 2 d
NULL 2 e
. . .
. . .
**As you see in the second table market_number1 never had product c

Just use LEFT OUTER JOIN for outformmobiledata table:
SELECT d.date, m.id, p.ProductName
FROM atb_markets m
CROSS JOIN atb_products p
LEFT OUTER JOIN outformmobiledata d
ON d.marketId = m.id
AND d.productId = p.id
ORDER BY m.id, p.ProductName

Related

Group and include all categories in SQL

I need to select all groups in a table for each category, even if that group is missing for a given category (and put 0 or NULL as value)
I need to do this via a SQL query (Impala).
An example is reported below (basically I need to dynamically display also the last row in the second table).
Category Group Amount Category Group Amount
+--------------------------------+ +--------------------------------+
A X 1 A X 1
A Y 2 A Y 2
A Z 5 -> A Z 5
B X 2 B X 2
B Y 3 B Y 3
B Z 0
Anyone knows how to achieve this? Thanks!
You need a Cross Join of the categories and the groups first and then a Left Join:
select c.category, g.group, coalesce(amount, 0)
from
( -- all categories
select distinct Category from tab
) as c
cross join -- create all possible combinations
( -- all groups
select distinct group from tab
) as g
left join tab as a -- now join back the amount
on c.category = a.category
and g.group = a.Group

How to Join with select top

I have a problem with the combination of multiple tables.
My SQL query:
SELECT *
FROM CRM.Bank as a
JOIN CRM.Documents as b ON a.Bank_ID = b.Documents_ID
JOIN CRM.Counterparties as c ON c.Counterparties_ID = b.Documents_ID
JOIN CRM.Items as d ON d.Document_tran_ID = b.Documents_ID
I have table CRM.Items, which has the following columns:
Item_ID Document_tran_ID Name
======= ================ ====
1 1 Advertising banner
2 1 Shipping costs
3 2 Garden tent
4 2 Additional fasteners
5 2 Shipping costs
And now I have a problem how to connect only the first items to the document (d.Document_tran_ID = b.Documents_ID ) ??
I know I should use SELECT TOP. However, I have a problem with creating the correct query
Expect the result in the form of:
Bank_ID Documents_ID Counterparties_ID Document_tran_ID Name
======= ============ ================= ================ ====
22 1 4 1 Advertising banner
23 2 20 2 Garden tent
24 3 21 3 Other
Only the first Item from the document is matched.
I think you can try to use CROSS APPLY join. In the inner query, you can apply order condition for selecting TOP row
SELECT *
FROM CRM.Bank as a
JOIN CRM.Documents as b ON a.Bank_ID = b.Documents_ID
JOIN CRM.Counterparties as c ON c.Counterparties_ID = b.Documents_ID
CROSS APPLY
(select top 1 * from CRM.Items i where i.Document_tran_ID = b.Documents_ID) as d
use sub-query for documents table
SELECT *
FROM CRM.Bank as a
JOIN ( select min(Document_tran_ID) as Documents_ID from CRM.Documents) as b ON a.Bank_ID = b.Documents_ID
JOIN CRM.Counterparties as c ON c.Counterparties_ID = b.Documents_ID
JOIN CRM.Items as d ON d.Document_tran_ID = b.Documents_ID

Order by and limit on a multiple left join - PostgresSQL and python

I have the following relations:
A Product have multiple Images
A Product can have multiple Categories
A Category can have multiple Products
I want to get:
only the 'short_name' from the first category
only the first image url order_by another parameter
I have the following SQL, in PostgreSql:
SELECT DISTINCT ON(I.product_id) P.id, P.name, P.short_description,
CAT.short_name AS category, I.url
FROM products_product AS P
LEFT JOIN products_product_categories AS RPC ON P.id = RPC.product_id
LEFT JOIN categories_category AS CAT ON RPC.category_id = CAT.id
LEFT JOIN products_productimage AS I ON I.product_id = P.id
WHERE (P.is_active = TRUE)
My issue is that I don't know to limit left join and order by, I try to add LIMIT 1
LEFT JOIN categories_category AS CAT ON RPC.category_id = CAT.id LIMIT 1
but it is not working, I receive a code error 'syntax error at or near "LEFT"'
Category table
id | category_name | category_short_name
1 catA A
2 catB B
3 catC C
Product table
id | product_name | product_desc
1 P1 lorem1
2 P2 lorem2
3 P3 lorem3
ManytoMany: product_category
id product_id category_id
1 1 1
2 2 1
3 1 2
4 3 3
5 3 3
Image table
id url product_id order
1 lo1 1 4
2 lo2 1 0
3 lo3 1 1
4 lo4 2 0
For Product with id1 I expect to get:
name: P1, desc 'lorem1', category short_name : cat A, image url lo2
DISTINCT ON makes no sense without ORDER BY. As you want two different orders (on i.order for images and on cat.id for categories), you must do this in separate subqueries.
select p.id, p.name, p.short_description, c.short_name, i.url
from products_product p
left join
(
select distinct on (pcat.product_id) pcat.product_id, cat.short_name
from products_product_categories pcat
join categories_category cat on cat.id = pcat.category_id
order by pcat.product_id, cat.id
) c on c.product_id = p.id
left join
(
select distinct on (product_id) product_id, url
from products_productimage
order by product_id, order
) i on i.product_id = p.id
where p.is_active
order by p.id;
Two alternatives to write this query are:
subqueries with fetch first row only in the select clause
lateral left joins on subqueries with fetch first row only

SQL Server: Subquery on a join

I have two tables with schema and data as below. Table A has an id and an associated name. Table B associates the id from Table A with a price and otherAttr. For each entry in Table A, there may be multiple entries with different prices in Table B, but otherAttr is the same for each entry.
Given an id for Table A, I would like to select the name, otherAttr, and the minimum price.
The below query returns multiple results, I need to write a query that will return a single result with the minimum price.
SELECT a.name, b.price, b.otherAttr
FROM A a
LEFT Join B b on b.idFromA = 1
WHERE a.id = 1
Table A Table B
id | name idFromA | price | otherAttr
-------- ---------------------------
1 | A 1 | 200 | abc
2 | B 1 | 300 | abc
1 | 400 | abc
2 | 20 | def
2 | 30 | def
2 | 40 | ef
I massively oversimplified my example. In addition to selecting the min price and otherAttr from Table B, I also have to select a bunch of other attributes from joins on other tables. Which is why I was thinking the Group By and Min should be a subquery of the join on Table B, as a way to avoid Grouping By all the attributes I am selecting (because the attributes being selected for vary programmatically).
The Actual query looks more like:
SELECT a.name, b.price, b.otherAttr, c.x, c.y, d.e, d.f, g.h....
FROM A a
LEFT Join B b on b.idFromA = 1
LEFT Join C c on something...
LEFT Join D d on something...
LEFT Join G g on something...
WHERE a.id = 1
To get this, you could use GROUP BY in an INNER query:
SELECT gd.name, gd.price,gd.otherAttr, c.x, c.y, d.e, d.f, g.h....
FROM
(SELECT a.id,a.name, MIN(b.price) as price, b.otherAttr
FROM A a
LEFT Join B b on b.idFromA = 1
WHERE a.id = 1
GROUP BY a.id,a.name,b.otherAttr) gd
LEFT Join B b on b.idFromA = 1
LEFT Join C c on something...
LEFT Join D d on something...
LEFT Join G g on something...
Try:-
SELECT a.name, MIN(b.price) minprice, b.otherAttr
FROM A a
LEFT Join B b ON a.Id = b.idFromA
GROUP BY a.name, b.otherAttr
HAVING a.id = 1
You could just do this instead:
SELECT a.name, MIN(b.price), MIN(b.otherAttr)
FROM TableA a
LEFT JOIN TableB b on b.idFromA = a.id
GROUP BY a.name
HAVING a.id = 1;
You need to inner join on price as well in addition to id on the subquery to intersect the right record(s) with the lowest price(s). Then TOP(1) will return only one of those records. You can avoid using TOP(1) if you can expand the conditions and group by fields in the subquery so you schema can assure only a single record is returned for that combination of attributes. Lastly, avoid left joins when intersecting sets.
SELECT TOP(1) p.id, p.price, b.OtherAttr
FROM B as b
INNER JOIN
(SELECT A.id, min(B.price) as price
FROM B
INNER JOIN A on A.id=B.idFromA and A.ID=1
GROUP BY A.id) as p on b.idFromA=p.id and b.price=p.price

sql query for 3 tables

i have 3 tables (A,B,C)
Table A -
ID Name
1 Sam
2 Manuel
3 Jane
Table B
ID Tab_A_ID Name
1 1 Meer
2 1 Kutti
3 2 Mikaro
Table C
ID Tab_B_ID Price
1 1 255.11
2 1 30.52
3 3 125.22
I need a query that shall pick up the top price for TableA-Name from TableC. So only 1 top price for 1 nae record.
e.g.-
Sam - 255.11
Manuel - 125.22
How can i get this?
To get the max price per entry in A:
SELECT a.Name,
MAX(c.price)
FROM a
INNER JOIN b
ON a.id = b.tab_a_id
INNER JOIN c
ON b.id = c.tab_b_id
GROUP BY a.id, a.name
To get the max price per entry A per entry B:
SELECT a.Name,
b.Name
MAX(c.price)
FROM a
INNER JOIN b
ON a.id = b.tab_a_id
INNER JOIN c
ON b.id = c.tab_b_id
GROUP BY a.id, b.id, a.name, b.name
Note that entries in A without corresponding entires in B or entries in B without corresponding entries in C will not appear in the result. Use LEFT JOIN if you want to include these in the result.