Flag missing record between 2 datasets - sql-server

The Title doesn't quite explain the issue, so I will give an example:
Table 1 (OrderNum, OrderLine) - All OrderLines ever created.
Table 2 (OrderNum, OrderLin) - Order Lines to be printed right now.
Table 2 always has matches in Table 1 as it is populated from table 1, but sometimes is missing a Line from an order in Table 1. For instance:
Table 1:
OrderNum, OrderLine
100, 1
100, 2
100, 3
100, 4
101, 1
102, 1
102, 2
Table 2:
OrderNum, OrderLine
100, 1
100, 2
100, 4
101, 1
In this instance I need to print all 4 lines of Order 100, with Line 3 flagged as missing, and all of Order 101, but none of order 102. Obviously my tables are much larger than this, but this sums up my dilemma. I cannot control how either table is populated.
If I LEFT Join Table 2 to Table 1 on OrderNum and OrderLine, then i get OrderNum 102 as well and I don't want that. If I Join on OrderNum Only then I get dupes of Order 100 for every line in Table 2, how do i find the 1 record not-duped?
Hope that makes sense as to my issue. I have tried Googling, but the terms are so generic I get way to many irrelevant results.

using a left join and exists():
select
t1.OrderNum
, t1.OrderLine
, Missing = case when t2.OrderLine is null then 'Flag' else '' end
from t1
left join t2
on t1.OrderNum = t2.OrderNum
and t1.OrderLine = t2.OrderLine
where exists (
select 1
from t2 i
where t1.OrderNum = i.OrderNum
)
rextester demo: http://rextester.com/SZPX33524
returns:
+----------+-----------+---------+
| OrderNum | OrderLine | Missing |
+----------+-----------+---------+
| 100 | 1 | |
| 100 | 2 | |
| 100 | 3 | Flag |
| 100 | 4 | |
| 101 | 1 | |
+----------+-----------+---------+

Related

Update hierarchy after deletion of row

I have a table that contains tree-like data (hierarchic design). Here is a small sample:
+----+----------+-----------+-------+----------+---------+
| ID | ParentID | Hierarchy | Order | FullPath | Project |
+----+----------+-----------+-------+----------+---------+
| 1 | null | 1 | 1 | 1 | 1 |
| 2 | null | 2 | 2 | 2 | 1 |
| 3 | 1 | 1.1 | 1 | 1-3 | 1 |
| 4 | 1 | 1.2 | 2 | 1-4 | 1 |
| 5 | 4 | 1.2.1 | 1 | 1-4-5 | 1 |
| 6 | 2 | 2.1 | 1 | 2-6 | 1 |
| 7 | null | 3 | 1 | 1 | 2 |
+----+----------+-----------+-------+----------+---------+
Project indicates which project owns the hierarchic dataset
ParentID is the ID of the parent node, it has a foreign key on ID.
Order is the rank of the element in one branch. For example, IDs 1, 2 and 7 are on the same node while 3 and 4 are in another.
FullPath shows the order using the ID (it's for system use and performance reasons).
Hierarchy is the column displayed to the user, which displays the hierarchy to the UI. It auto calculates after every insert, update and delete, and it's the one I'm having issues.
I created a procedure for deletion elements in the table. It receives as input the ID of the element to delete and deletes it, along with it's children if any. Then, it recalculates the FullPath and the Order Column .That works.
Problems is when I try to update the Hierarchy column. I use this procedure:
SELECT T.ID,
T.ParentID,
CASE WHEN T.ParentID IS NOT NULL THEN
CONCAT(T1.Hierarchy, '.', CAST(T.Order AS NVARCHAR(255)))
ELSE
CAST(T.Order AS NVARCHAR(255))
END AS Hierarchy
INTO #tmp
FROM t_HierarchyTable T
LEFT JOIN t_HierarchyTable T1
ON T1.ID = T.ParentID
WHERE Project = #Project --Variable to only update the current project for performance
ORDER BY T.FullPath
--Update the table with ID as key on tmp table
This fails when I delete items that have lower order than others and they have children.
For example, if I delete the item 3, item 4 Hierachy will be corrected (1.1), BUT its child won't (it will stay at 1.2.1, while it should be 1.1.1). I added the order by to make sure parents where updated first, but no change.
What is my error, I really don't know how to fix this.
I managed to update the hierarchy with a CTE. Since I have the order, I can append it to Hierarchy, based on the previous branch (parent) who is already updated.
;WITH CODES(ID, sCode, iLevel) AS
(
SELECT
T.[ID] AS [ID],
CONVERT(VARCHAR(8000), T.[Order]) AS [Hierarchy],
1 AS [iLevel]
FROM
[dbo].[data] AS T
WHERE
T.[ParentID] IS NULL
UNION ALL
SELECT
T.[ID] AS [ID],
P.[Hierarchy] + IIF(RIGHT(P.[Hierarchy], 1) <> '-', '-', '') + CONVERT(VARCHAR(8000), T.[Order]) AS [Hierarchy],
P.[iLevel] + 1 AS [iLevel]
FROM
[dbo].[data] AS T
INNER JOIN CODES AS P ON
P.[ID] = T.[ParentID]
WHERE
P.[iLevel] < 100
)
SELECT
[ID], [Hierarchy], [iLevel]
INTO
#CODES
FROM
CODES

Displaying data in a different manner

So I have a table that binds ProductId and GroupId. The product can be assigned to all of 5 groups (1-5).
If the product doesn't exist in the table, it's not assigned to any of the group
ProductId | GroupId
-------------------
100 | 1
100 | 2
200 | 1
200 | 2
200 | 3
200 | 4
200 | 5
Taking a look at this table, we know that Product that goes by id 100 is assigned to 2 groups (1,2) and the product of id 200 is assigned to 5 groups (1-5).
I'm trying to write a query that will display each product in separate row, together with columns for all of the 5 groups and a bit value that contains information if the product belongs to the group or not (0,1). A visualization of the result I need:
ProductId | IsGroup1 | IsGroup2 | IsGroup3 | IsGroup4 | IsGroup5
-----------------------------------------------------------------
100 | 1 | 1 | 0 | 0 | 0 -- this belongs to groups 1, 2
200 | 1 | 1 | 1 | 1 | 1 -- this belongs to all of the groups
I know I could probably solve it using a self join 5 times on each distinct product, but I'm wondering if there's a more elegant way of solving it?
Any tips will be strongly appreciated
You could use a pivot. Since you only have 5 groups you don't need a dynamic pivot.
DB FIDDLE
select
ProductId
,IsGroup1 = iif([1] is null,0,1)
,IsGroup2 = iif([2] is null,0,1)
,IsGroup3 = iif([3] is null,0,1)
,IsGroup4 = iif([4] is null,0,1)
,IsGroup5 = iif([5] is null,0,1)
from
(select ProductID, GroupId from mytable) x
pivot
(max(GroupId) for GroupId in ([1],[2],[3],[4],[5])) p

SQL'Where IF' statement

I'm currently trying to get a value from two tables. I have table A and Table B
Table A holds the main data of which i will be using to display my results.
Table B hold data which holds data if a certain condition in table A is met.
Table A
UserID |Date |useExternalInfo |Address |Hours
1 |12/12/2014 |0 |myaddress 1 | 4
2 |13/12/2014 |0 |myaddress 2 | 9
3 | 14/12/2014 |1 | myaddress 3| 12
Table B
ID | Date |Hours
1 | 12/12/2014 | 10
2 | 13/12/2014 | 6
3 | 14/12/2014 | 7
3 | 15/12/2014 | 8
the query i'm trying to get is
select UserID, Date, (hours from TableA if UseExternalinfo is 0, and from hours TableB if useextername)
from TableA
where
TableA.Date = '14/12/2014' if (ExternalUse = 0) or TableB.Date = '14/12/2014' if (ExternalUse = 1)
and user = 3
for example if date is '14/12/2014' and user = 3 i need to find the hours of 7 not 12 because use external is 1.
Not sure if that makes sense i'm just basically trying to get a 'where if' statement if such a thing exists. Could someone point me in the right direction how i could achieve this please.
thanks
select UserID,
Date,
case when UseExternalInfo = 1 then tableA.hours else tableB.hours end
from TableA left join tableB on TableA.UserId = tableB.Id
where TableA.Date = '14/12/2014' and
TableB.Date = '14/12/2014' and
user = 3

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

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