join two tables on id from json column in postgresql - sql

Trying to accomplish this;
TABLE PRODUCTS
id | product_id | product
------------------------------
1 | 123| acme widget
------------------------------
2 | 456| acme gadget
TABLE ORDERS
id | lineItems
------------------------------
1 | [{ id: 123, quantity: 10}, { id: 456, quantity: 5}]
USING
SELECT a.*
FROM orders a
LEFT OUTER JOIN products b ON b.product_id = a.products -> 'id'
in order to return
id | product_id | product | quantity
------------------------------------------
1 | 123 | acme widget | 10
------------------------------------------
1 | 456 | acme gadget | 5

I solved this. I created a view
CREATE VIEW view_lineItems
AS
SELECT
aa.id AS order_id,
-- (p->>'id')::bigint AS product_id,
bb.*,
(p->>'quantity')::integer AS quantity
FROM public.pim_orders aa, json_array_elements(aa.products) p
LEFT OUTER JOIN pim_batch bb ON bb.product_id = (p->>'id')::bigint
ORDER BY aa.id;
and then joined that view to the original query (stored procedure) and called in the columns I needed.
because of the nature of the line items view i wasn't able to create a primary key - which I would need for a materialized view - as it is possible for there to be multiple products as each line item, and also possible for the same product to be listed multiple times for different orders.
but this worked fine for my needs :) I'm now able to decide which view I'll join specific tables for returned results - whether on the order, or on the line item.
cheers!

Related

SQL query to match similar customers

I'm trying to find the query in order to match similar customers.
To simplify the situation consider this scenario:
I have a table which contains a customer name and product purchased.
customer name can have multiple purchases of same and different products.
So firstly I can take distinct customer name and product name, so I see all customers and all products they purchased at least once.
Now I want a query to show me a sort of matching customers, according to the product they both purchased, so I want to count the similar products they purchased.
So I want to see for each pair of customers (pairing all the table) the amount of similar product they purchased.
Lets say the raw data is:
CustomerName | ProductName
A | 1
A | 2
A | 1
A | 3
B | 1
B | 2
B | 4
C | 2
Then I want to see the result of:
CustomerName1 | CustomerName2 | CountSimilarity
A | B | 2
A | C | 1
B | C | 1
And so on for all pairs of customers that have at least 1 similar product purchasing
Any suggestions how to approach this query?
The environment is SQL Server.
Thanks
Here is a self join approach:
SELECT t1.CustomerName, t2.CustomerName, COUNT(*) AS CountSimilarity
FROM yourTable t1
INNER JOIN yourTable t2
ON t1.ProductName = t2.ProductName
WHERE
t1.CustomerName < t2.CustomerName
GROUP BY
t1.CustomerName, t2.CustomerName;
Two records are joined together above if their products match. Note that the inequality in the WHERE clause ensures that customer pairs do not appear in duplicate.

Database Design: Inventory with multilevel packaging

I'm trying to develop a database for my inventory. But I have no idea how to keep track of multilevel packaging.
For example:
I currently have a products and positions table
products
Id | Name
================
1013 | Metal
1014 | Wood
positions
id | Name
================
1 | 1-1-1-1
2 | 1-1-1-2
And my inventory table I was thinking of doing something like this:
Let's say I stored 1 box with 1000 Metal and 1 box with 500 Wood at position 1-1-1-1
ItemId | ProductId | Quantity | PositionId
==========================================
1 | 1013 | 1000 | 1
2 | 1014 | 500 | 1
So I'll label those two boxes with a barcode 1 and 2 respectively, so if I scan them, I can check this table to see the product and quantity inside them.
But I can also put these 2 boxes (1 and 2) inside another box (let's call it box 3), which would generate a new barcode for it that, if scanned, will show both previous boxes and its items. And store this box 3 in another position
And I can also put this box 3 inside a pallet, generating a new code and so on. So basically I can multilevel package N times.
What is the best table structure to keep track of all of this? Thanks in advance for any help!
I would add another column to the products table, make it a BIT and maybe call it BOM, BillOfMaterials, or whatever makes sense to you
So your products Table would look like this
Then you could create another table called BillOfMaterials
Quantity is how many of your products are needed to make up your new product. So for this example 2 metal and 1 wood make a pencil.
I was able to make a good structure:
My products and positions are the same but I created a stock table like:
id | product_id | amount | parent_id | position_id
=====================================================
1 | 1013 | 1000 | 4 | 1
2 | 1013 | 1000 | 4 | 1
3 | 1014 | 500 | 4 | 1
4 | 1234 | NULL | NULL | 1
The 1234 (random id) is a box that contains 2000 metal and 500 wood. I dont save this box in the product table.
When I scan the box with id 3, I perform a recursive cte query:
with recursive bom as (
select *, 1 as level
from testing.stock
where id = '4' #scanned id
union all
select c.*, p.level + 1
from testing.stock c
join bom p on c.parent_id = p.id
)
select product_id as product, sum(amount), position_id
from bom b
left join testing.product pd on b.product_id = pd.id
where pd.id is not null
group by product_id, position_id
which returns:
sum | product | position
2000 | 1013 | 3
500 | 1014 | 3
to get by position I just run a variation of the above query. To perform an update I get the Ids inside that box and run a
update testing.stock set position = '2' where id in (#variation of above query)
I hope this helps someone. This works for N packaging level

MS Access 2016 - Pull client name from separate table in complex query

I have three tables for vulnerability scanning jobs: customers, authorization forms, and scans. Relationships are one to many from left to right. I previously had scans directly related to clients, but implemented the forms table to add the ability to prevent scanning without authorization. I have the below query which pulls the dates of the most recent and next coming scans (huge thanks to #donPablo), but when I made the change in tables I'm no longer pulling the correct data from the customers table. I'm not exactly sure how to fix it.
SELECT u.Customer_Company, z.*
FROM (Select
NZ(a.Scan_Data.Customer_ID, b.Scan_Data.Customer_ID) as Customer,
aPast as Past,
aFuture as Future,
DATEDIFF("d", aPast, aFuture) as Difference
FROM
(Select Scan_Data.Customer_ID, Max(Scan_Date) as aPast from Scan_Data where Scan_Date <= DATE() Group By Scan_Data.Customer_ID) a
LEFT JOIN
(Select Scan_Data.Customer_ID, Min(Scan_Date) as aFuture from Scan_Data where Scan_Date > DATE() Group By Scan_Data.Customer_ID) b
ON a.Scan_Data.Customer_ID = B.Scan_Data.Customer_ID
UNION
Select
NZ(a.Scan_Data.Customer_ID, b.Scan_Data.Customer_ID) as Customer,
aPast as Past,
aFuture as Future,
DATEDIFF("d", aPast, aFuture) as Difference
FROM
(Select Scan_Data.Customer_ID, Max(Scan_Date) as aPast from Scan_Data where Scan_Date <= DATE() Group By Scan_Data.Customer_ID) a
RIGHT JOIN
(Select Scan_Data.Customer_ID, Min(Scan_Date) as aFuture from Scan_Data where Scan_Date > DATE() Group By Scan_Data.Customer_ID) b
ON a.Scan_Data.Customer_ID = B.Scan_Data.Customer_ID
) AS z LEFT JOIN Customer_Data AS u ON cint(z.Customer) = cint(u.Customer_ID);
In this query the Scan_Data.Customer_ID winds up being the FormID and it then pulls the customer's name based on the FormID. I fixed it in my other queries by doing a double inner join to pull the actual CustomerID based on the FormID, but I can't get that to work here because of the existing joins. Form_Data.Customer_ID is the way it's identified in the Form table. All IDs in their primary tables are autonumber generated PKs.
Customer_Data table:
.Customer_ID | .Customer_Name | etc.
1 | Microsoft |
2 | Reddit |
Form_Data table:
.Form_ID | .Signature_Date | .Expiration_Date | .Customer_ID
1 | 01-Jan-19 | 01-Jan-20 | 2/Reddit
2 | 15-May-18 | 15-May-21 | 1/Microsoft
Scan_Data table:
.Scan_ID | .Scan_Title | .Scan_Date | .Customer_ID
1 | First MS 19052018 | 19-May-18 | 1/2/Reddit
2 | First R 05012019 | 05-Jan-19 | 2/1/Microsoft
The above Scan_Data shows the problem I'm having. The numbers in the Scan_Data.Customer_ID field are the PKs from the other two tables. The .Customer_ID field is pulling the customer ID based upon the form ID and not the actual customer ID. It should show like this:
.Scan_ID | .Scan_Title | .Scan_Date | .Customer_ID
1 | First MS 19052018 | 19-May-18 | 2/1/Microsoft
2 | First R 05012019 | 05-Jan-19 | 1/2/Reddit

Sql request return wrong result

I have issue with a SQL request or my database structure. I would like to have supplier/customer database. As the ordered price products and sold products can change, I'd like to keep an historic on each order / sales to create in the future statistics on them. I'm at the beginning of my project and using 6 simple tables :
Client | OrderC | OrderDetail
-----------------+------------------+--------------------------------------
Client_ID (Pk) | Order_ID (Pk) | OrderD_ID (Pk)
Name | Client_ID (Fk) | Order_ID (Fk)
| date | Product_ID (Fk)
| | Qty
| | PU_Vte (sales price)
| | For_Cmd_ID (Fk from For_ID-Cmd table Forever_cmd)
_________________|__________________|_____________________________________
|(supplier database)| (detail of supplier order)
Product | Forever_cmd | For_Ord_Detail
----------------+-------------------+----------------------------------------------------
Product_ID (Pk) | For_ID_CMD (Pk) | For_Det_Id (Pk)
Name | date |ID_cmd_For (Fk from For_ID-CMD on Forever_cmd table)
| |Product_ID (Fk source Product_ID on Product table)
| | Qte (= quantity)
| | PUHA (= supplier price)
All is working fine until I have similar products on different supplier orders. The result of my sql request create extra result for the customers.
Example:
I create 2 supplier orders (S1 and S2) containing same product ID (P1) with different supplier prices. I create a customer order and I choose to sale product P1 from S1.
When I query to obtain a view by client with products and to track them from supplier order, the result add all similar products from all supplier orders to the customers. Instead of only the products ordered by the customers.
I don't know if it is from my query or database consistency.
Here is my sql request :
SELECT C.Name,
O.order_id,
F.For_ID_CMD,
P.Name,
D.Qte,
D.PU_Vte,
X.PUHA
FROM Client AS C
JOIN OrderC O ON C.Client_ID = O.Client_ID
JOIN OrderDetail D ON O.Order_ID = D.Order_ID
JOIN For_Ord_Detail X ON D.Product_ID = X.Product_ID
JOIN Forever_Cmd F ON F.For_ID_CMD = X.ID_cmd_For
JOIN Product P ON D.Product_ID = P.Product_ID;
Could someone please help me ?
First, a quick code review:
"date" is a special word. I'd recommend not using it as a column header. Be a bit more descriptive about the type of date these are. OrderC_date and Forever_cmd_date. Or OC_date, FC_date.
You've used both "Qty" and "Qte" for quantity. Be consistent. Or even better, be more descriptive of what those quantities are.
For your duplicates, remove your JOINs one at a time until you figure out which one is causing the dupes. I'm betting you need to narrow your JOIN criteria on one of those tables.
===========================================================
EDIT
This should point you in the right direction to find your duplicates.
SQL Fiddle
MS SQL Server 2014 Schema Setup:
CREATE TABLE Product ( Product_ID int, Name varchar(20) ) ;
INSERT INTO Product (Product_ID, Name)
VALUES
(1, 'Widget1')
, (2, 'Widget2')
;
CREATE TABLE Forever_cmd ( For_ID_CMD int, [date] date ) ;
INSERT INTO Forever_cmd ( For_ID_Cmd, [date] ) /* Why a date? */
VALUES (1,'10/26/1985'), (2,'6/27/2012');
CREATE TABLE For_Ord_Detail ( For_Det_Id int, ID_cmd_For int, Product_ID int, Qte int, PUHA decimal(10,2) ) ;
INSERT INTO For_Ord_Detail ( For_Det_Id, ID_cmd_For, Product_ID, Qte, PUHA )
VALUES
(1,1,1,10,2.00)
, (2,1,2,10,12.00)
, (3,2,2,20,20.00)
;
Query 1:
SELECT F.For_ID_CMD, P.Name, X.PUHA
FROM Product P
LEFT OUTER JOIN For_Ord_Detail X ON P.Product_ID = X.Product_ID <<<<<
LEFT OUTER JOIN Forever_Cmd F ON X.ID_cmd_For = F.For_ID_CMD
Results:
| For_ID_CMD | Name | PUHA |
|------------|---------|------|
| 1 | Widget1 | 2 |
| 1 | Widget2 | 12 | << Why did this "duplicate"?
| 2 | Widget2 | 20 | << Why did this "duplicate"?
Hint: How do you determine which supplier you get your product from if multiple suppliers have the same product?

Using foreign key

I am new to foreign key, but I understand the concept very well.
I have found lot of documentation on how to create / delete them but not how to use them. My schema is as follows.
Stock table:
PartID | Model | Type | Vendor
------------------------------
1 | DDr2 | RAM | shop1
2 | DDr3 | RAM | shop1
3 | WD1 | HDD | shop2
4 | WD2 | HDD | shop2
Then product Table
ProdID | Name | PartID1 | PartID2 ...
1 | PC1 | 1 | 2
2 | PC1 | 3 | 4
How do I use select to get
| PC1 | DDr2 | DDR3 |
| PC1 | WD1 | WD2 |
with PartID2 and PartID3 foreign key linked to PartID primary key?
The concept of Foreign Keys is to link the IDs in one table to the lisk of unique IDs in another. In your example, you have unique parts with unique IDs and Products that can use those parts, so in your product table, you could have multiple part IDs being used in multiple rows.
Foreign Keys are used to keep referential integrity in your database, you can use joins to get the Data you want:
SELECT A.NAME,
B.Model,
C.Model
FROM PRODUCTS A
INNER JOIN PARTS B ON B.PARTID1 = A.PARTID
INNER JOIN PARTS C ON C.PARTID1 = A.PARTID
WHERE A.PRODID = 1
The short answer is you could do
select p.name, a.model as part1, b.model as part2, c.model as part3
from product p, stock a, stock b, stock c
where p.partid1 = a.partid and p.partid2 = b.partid and p.partid3 = c.partid
The longer answer is that this isn't really a good table design for what you're trying to do. It assumes that you always have a fixed number of parts for any item (or at least no more than some fixed number). A better design would be:
Part Table:
partID | model | type | vendor
Product Table:
productID | name
Product_Parts Table:
productID | partID
where productID in Product_parts is a foreign key into Product and partID is a foreign key into the Part table.
SELECT s1.Name, p1.Model, p2.Model FROM stock st
INNER JOIN product p1
ON st.PartID1 = p1.PartID1
INNER JOIN product p2
ON st.PartID2 = p2.PartID1
Take one JOIN at the time first join stock and parts table
then again join result of this join to parts table.
SQL parser will use parts table as two separate tables an so you can have two results from same tabe in single row.
you can join in a table more than once in the same sql statement. in this case, you need to join your stock table twice, once to get the name of each part in your product.
SELECT pr.ProdID, s1.Model, s2.Model
FROM Product pr, Stock s1, Stock s2
WHERE pr.PartID1 = s1.PartID
AND pr.PartID2 = s2.PartID
Using a LEFT OUTER JOIN means that the product will still be returned event if the Part1ID or Part2ID values are set to NULL.
SELECT P.Name,
S1.Model,
S2.Model
FROM Product P
LEFT OUTER JOIN Stock S1 ON P.PartID1 = S1.PartID
INNER JOIN Stock S2 ON P.PartID2 = S2.PartID