Setting 0 when NO rows from select statement with multiple tables - sql-server

Let's say I have this table
(Simplified, my query have more tables involved)
ConsignmentItem
ID |Item code| Name | Quantity
1 | 00000 | A | 3
2 | 11111 | B | 2
And this other table
PickingItem
ID |ConsignmentID|Quantity
1 | 1 | 1
What my query does is to join both tables and print the amount of products ordered and the amount of products already registered. I would like to get as result the following table
Item Code| Name | Quantity_Ordered | Quantity_Registered
00000 | A | 3 | 1
11111 | B | 2 | 0
My query works whenever the item exist on "PickingItem", if it doesn't it prints the same "Quantity_Registered" as the above row, using my query I get as result the following table
Item Code| Name | Quantity_Ordered | Quantity_Registered
00000 | A | 3 | 1
11111 | B | 2 | 1(this is wrong)
This is the query i'm using
SELECT C.Barcode AS 'Item Code',C.ProductName AS 'Name', C.Quantity AS 'Quantity_Ordered', ISNULL(P.Quantity,0) AS 'Quantity_Registered'
FROM PICKING.OrderPickingItem P
JOIN PICKING.OrderPicking OP ON P.PickingID = OP.PickingID
JOIN ORDERS.ConsignmentItem C ON OP.ConsignmentID = C.ConsignmentID
WHERE P.PickingID = 1 --For testing
Anyone know what could I do to, if the product doesn't exist on OrderPickingItem, then set P.Quantity = 0 for that specific row?
EDIT:
Structure of the tables
OrderPickingItem
PickingItemID PK
PickingID FK
ConsignmentItemID FK
Quantity
--other not used columns for this query
OrderPicking
PickingID PK
ConsignmentID FK
--other not used columns for this query
ConsignmentItem
ConsignmentItemID PK
ConsignmentID FK
Barcode
Quantity
ProductName
--other not used columns for this query

You are obviously looking for an outer join:, you want to show ConsignmentItem records even when there is no matching picking.
select
C.Barcode AS "Item Code",
C.ProductName AS "Name",
C.Quantity AS "Quantity_Ordered",
ISNULL(P.Quantity, 0) AS "Quantity_Registered"
from ORDERS.ConsignmentItem c
left join PICKING.OrderPicking op on OP.ConsignmentID = C.ConsignmentID
left join PICKING.OrderPickingItem P on P.PickingID = OP.PickingID
and P.ConsignmentItemID = C.ConsignmentItemID;

Do an outer join and something like ifnull(p.id, 0) for the quantity in the select clause.

Related

Cross join, display table 2 column value base on table1

I have two tables in SQL Server, Say in table1 I have two columns Key1Display and Key2Display, they are of datatype bit and used to control whether to display the values in table2, and table 2 will have 2 columns Key1 and Key2.
What I am trying to achieve is a sort of cross join, say if table 1 has 3 rows:
| Key1Display | Key2Display |
+---------------------+------------------+
| 0 | 1 |
| 1 | 0 |
| 1 | 1 |
Say in table 2 there are 2 rows
| Key1 | Key2 |
+---------------------+------------------+
| Row1Key1value | Row1Key2value |
| Row2Key1value | Row2Key2value |
Then based on these two tables, I want to have a query to display 6 (2*3) rows and 1 column of results like this:
null:Row1Key2value
Row1Key1Value:null
Row1Key1Value:Row1Key2value
null:Row2Key2value
Row1Key2Value:null
Row1Key2Value:Row2Key2value
So something like:
select
case when t1.Key1Display = 1 then coalesce(t2.Key1,'??') else 'null' end
+ ':' + case when t1.Key2Display = 1 then coalesce(t2.Key2,'??') else 'null' end
-- And so on for as many keys as you have
from table1 t1
cross join table2 t2

In PostgreSQL: how to transform an array field containing FKs to array of names from referenced table?

My database has a table of Products, like so:
PRODUCTS
----------------------------
id | name | suppliers
----------------------------
1 | widget | {1,2}
2 | gizmo | {1}
3 | geegaw | {3}
4 | tchotchke | null
The suppliers column contains an array (numeric[]) of IDs belonging to a table of suppliers:
SUPPLIERS
------------
id | name
------------
1 | alpha
2 | beta
3 | gamma
How can I write a query that will return the contents of PRODUCTS except with an array of supplier names instead of supplier ID numbers? Result should look like this:
-----------------------------------
id | name | suppliers
-----------------------------------
1 | widget | {'alpha','beta'}
2 | gizmo | {'alpha'}
3 | geegaw | {'gamma'}
4 | tchotchke | null
Succinct and efficient methods would be preferred, but readability/understandability are also nice.
Edit: This isn't a duplicate of the linked question, although that question does involve the unnest operation, it doesn't re-aggregate the result. The answer to this question makes a new contribution to the site.
select t1.id, t1.name, array_agg(s.Name) as Suppliers
from Products t1
left join lateral (
select unnest(suppliers) as supplierId from myProducts t2
where t1.id = t2.id) as t on true
left join Suppliers s on s.Id = t.supplierID
group by t1.id, t1.name;
What was I thinking. Here is a better version:
select p.id, p.name, array_agg(s.Name) as Suppliers
from (select *,unnest(suppliers) as supplierId from Products) p
left join Suppliers s on s.Id = p.supplierID
group by p.id, p.name;

SQL Query to get data based on multiple filters

I have following Product table and ProductTag tables -
ID | Product
--------------
1 | Product_A
2 | Product_B
3 | Product_C
TagID | ProductID
----------------------
1 | 2
1 | 3
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2
Now I need a SQL query that return all products list which are having both Tag 1 and 2. Result should be as given below -
ProductID | Product
------------------------
2 | Product_B
3 | Product_C
Please suggest how can i write a MS SQL query for this.
SELECT p.ID, p.Product
FROM Product p
INNER JOIN ProductTag pt
ON p.ID = pt.ProductID
WHERE pt.TagID IN (1, 2) -- <== Tags you want to find
GROUP BY p.ID, o.Product
HAVING COUNT(*) = 2 -- <== tag count on WHERE clause
however, if TagID is not unique on every Product, you need to count only the distinct product.
HAVING COUNT(DISTINCT pt.TagID) = 2
More on: SQL of Relational Division

Postgres: Query that can filter during table join

I have a postgres database with duplicated entries on one of the table. I would like to show the created_by columns
Table1
id | number
1 | 123
2 | 124
3 | 125
4 | 126
Table2
id | number | created_on
1 | 123 | 3/29
2 | 123 | 4/3
3 | 124 | 3/31
4 | 124 | 4/1
On table 2 number are duplicated. I would like to form a single query to list the following:
id | number | created_on
1 | 123 | 4/3
2 | 124 | 4/1
For duplicated entries only the latest entry will be included. How could I form that SQL query?
SELECT DISTINCT ON (Table1.number) Table1.id, Table2.number, Table2.create_on FROM Table1
JOIN Table2 ON Table1.number=Table2.number
ORDER BY Table2.create_on;
Actually I tried putting 'DISTINCT ON' and 'ORDER BY' in a single query (with JOIN) it gives me the following error:
SELECT DISTINCT ON expressions must match initial ORDER BY expressions
The columns in DISTINCT ON() have to be the first ones in the ORDER BY query, also if you want the latest created_on date you should order by created_on DESC
SELECT DISTINCT ON (Table1.number) Table1.id, Table2.number, Table2.created_on
FROM Table1
JOIN Table2
ON Table1.number=Table2.number
ORDER BY Table1.number,Table2.created_on DESC;
http://sqlfiddle.com/#!12/5538a/2
As you said in the comment: created_on=date_trunc('day', now()), so the data type of the field created_on is timestamp. Here is what you can do:
SELECT table_1.id, table_1.number, max(created_on) as created_on
FROM table_1
inner join table_2 using(number)
group by table_1.id, table_1.number

how should i join these five tables & SUM multiple columns from multiple tables

I have a database with 5 tables that have related data..
it looks something like this..
The table "associate_payin_ad" stores the date of registration & annexure id. Physically an Annexure is just a piece of paper which can have zero or more "Payin" or "Associate" entries..
Also 'payin' & 'associate' tables have multiple mode's of payment (like cash, cheque, bdcash, bdcheque) for the [amount] & [payment] column.. there are separate tables present for bycash, bycheque, bybdcash & bybdcheque, I have shown just the 'bycash' tables...
If the tables are filled with the following below given data..
[associate_payin_ad] Table:
adid | date_register | annexure_id
1 | 05/12/2011 | 1
2 | 05/12/2011 | 2
3 | 06/12/2011 | 1
4 | 07/12/2011 | 1
[payin] Table:
fid | amount | adid
1 | 10000 | 1 [this entry was made on 05/12/2011 in annexure no 1]
2 | 10000 | 1 [this entry was made on 05/12/2011 in annexure no 1]
3 | 40000 | 2 [this entry was made on 05/12/2011 in annexure no 2]
4 | 10000 | 4 [this entry was made on 07/12/2011 in annexure no 1]
[payin_bycash] Table:
fid | bycash
1 | 10000
2 | 10000
3 | 40000
4 | 10000
[associate] table...
aid | payment | adid
1 | 200 | 1 [this entry was made on 05/12/2011 in annexure no 1]
2 | 200 | 3 [this entry was made on 06/12/2011 in annexure no 1]
[associate_bycash] table...
aid | bycashajf
1 | 200
2 | 200
I need the SUM of [payin_bycash.bycash] & [associate_bycash.bycashajf] for a particular date range.. (for eg. 05/12/2011 to 07/12/2011)
date_register | amount
05/12/2011 | 60200
06/12/2011 | 200
07/12/2011 | 10000
I have been running around in circles since yesterday trying to figure out the appropriate query.. the best I could come up with it is this, but in vain:
SELECT apad.date_register,
SUM(ISNULL(pica.cash_in_hand, 0)) + SUM(ISNULL(aca.bycashajf, 0)) AS amount
FROM associate_payin_ad AS apad LEFT OUTER JOIN
payin AS pi ON apad.adid = pi.adid INNER JOIN
payin_bycash AS pica ON pi.fid = pica.fid
LEFT OUTER JOIN associate AS asso ON apad.adid = asso.adid INNER JOIN
associate_bycash AS aca ON asso.aid = aca.aid
WHERE (apad.date_register BETWEEN #date_initial AND #date_final)
GROUP BY apad.date_register
The above query returns me just this..
date_register | amount
05/12/2011 | 20400
What am i doing wrong?
thnx in advance
You can't mix inner and outer joins like that. When you use a left outer join, it will return null records in the right hand table to ensure that all rows from the left hand table are returned as expected. However, if you then try to join the right hand table to another table using an INNER join, the null records will be filtered out as you won't have matching null records in the other table.
In your case, this is happening when you join to payin. You'll get a row for aid=3, but then that row is filtered out when you try to join to payin_bycash, as aid=3 doesn't exist in payin.. Same problem for your join to associate.
The best way to around this problem is to left join to a subquery (or you could do it with a CRE).. Try this:
SELECT apad.date_register,
SUM(ISNULL(pica.cash_in_hand, 0)) + SUM(ISNULL(aca.bycashajf, 0)) AS amount
FROM associate_payin_ad AS apad
LEFT OUTER JOIN
(
SELECT payin_bycash.cash_in_hand
FROM payin
INNER JOIN payin_bycash ON payin.fid = payin_bycash.fid
) pi ON apad.adid = pi.adid
LEFT OUTER JOIN
(
SELECT associate_bycash.bycashajf
FROM associate
INNER JOIN associate_bycash ON associate.aid = associate_bycash.aid
) asso ON apad.adid = asso.adid
WHERE (apad.date_register BETWEEN #date_initial AND #date_final)
GROUP BY apad.date_register
Also, have a read of this: http://weblogs.sqlteam.com/jeffs/archive/2007/10/11/mixing-inner-outer-joins-sql.aspx

Resources