Find missing rows for a column in the same table - sql-server

I need to find the missing rows that exist in a table of our SQL Server. I have a list of 3 items, for example, that must always exist for each header_id as individual rows with the appropriate product_id.
So ideally the data should be appear as this:
header_id product_id product_group_id
10 Main Product 7
10 Bundle Item 1 50
10 Bundle Item 2 50
10 Bundle Item 3 50
However, due to the process in which the information is added to the database it requires each line to be added individually by the data entry person. As a result we are seeing values such as this where "Bundle Item 2" is missing:
header_id product_id product_group_id
10 Main Product 7
10 Bundle Item 1 50
10 Bundle Item 3 50
The following script displays the header_id's that are missing a missing product_id however it only provides the product_id of "Main Product".
SELECT header_id, product_id, product_group_id
FROM Table1
WHERE
(product_id = 'Main Product')
AND (header_id NOT IN (SELECT product_id
FROM table1 AS table1_1
WHERE (product_id = 'Bundle Item 2')))
I know I can use unions to group multiple queries and force a description value such as this below but would prefer an alternative way if possible.
SELECT header_id, product_id, product_group_id, 'Bundle Item 2' as Description
FROM Table1
WHERE
(product_id = 'Main Product')
AND (header_id NOT IN (SELECT product_id
FROM table1 AS table1_1
WHERE (product_id = 'Bundle Item 2')))
UNION
SELECT header_id, product_id, product_group_id, 'Bundle Item 3' as Description
FROM Table1
WHERE
(product_id = 'Main Product')
AND (header_id NOT IN (SELECT product_id
FROM table1 AS table1_1
WHERE (product_id = 'Bundle Item 3')))

yup,you hv to redesign your DB.There can be so many data namely anything.how do i know under which main product id comes what all sub product id.and if anything is missing .I cannot hard code 'Bundle Item 2'.
If each main product will have at least 3 sub product then using row_number you can find...what ?
Declare #t table (header_id int,product_id varchar(50),product_group_id int)
insert into #t
select 10, 'Main Product', 7 union all
select 10, 'Bundle Item 1' , 50 union all
select 10, 'Bundle Item 2' , 50 union all
select 10, 'Bundle Item 3' , 50
select *,row_number()over(order by product_id)rn from #t where product_group_id<>7

If you know you need exactly those four products, then you can handle this by creating all possible combinations of header_id and product. Then use left outer join to find the ones that are missing:
with prods as (
select 'Main Product' as product union all
select 'Bundle Item 1' union all
select 'Bundle Item 2' union all
select 'Bundle Item 3'
),
allheaderprods as (
select distinct header_id, product
from prods cross join
table1
)
select ahp.*
from allheaderprocs ahp left outer join
header h
on ahp.header_id = h.header_id and
ahp.product = h.product
where ahp.header_id is null;

Related

How to get all orders with maximum update_count value only?

I have laboratory orders system and these orders can be update more than one time for example
order_id test_id update_count
10 1 1
10 1 2
10 1 3
11 2 1
11 5 1
12 3 1
12 3 2
I want to select all orders without duplicates and select orders with maximum update count
I tried a lot and i checked WITH clients as and self join but always select statement returned all rows with all update counts not only maximum update count for each order_id
This is my Select statement:
SELECT LAB_RESULTS.ORDER_ID as 'Order Number'
,LAB_RESULTS.PATIENT_NO as 'Patient No'
,Patients.Patient_Name as 'Patient Name'
,Patients.Age as 'Patient Age'
,LabTests.TestName as 'Test Name'
,LAB_RESULTS.RESULT_NUMBER as 'Result'
,LAB_RESULTS.RESULT_REPORT as 'Text Result'
,LAB_RESULTS.APPROVED_DATE as 'Approved_Date'
,LAB_RESULTS.REQ_FORM_NO as 'Request Form Number'
,LAB_RESULTS.CUSTID as 'Customer Id'
,Machines.Machine_name as 'Machine Name'
,LAB_RESULTS.SAMPLE_ID as 'Sample Id'
,LAB_RESULTS.packageid as 'package id'
,LAB_RESULTS.GROUPID as 'group id'
,LAB_RESULTS.EXAMINED_BY as 'Examined By'
,LAB_RESULTS.EXAMINED_DATE as 'Examined Date'
,LAB_RESULTS.APPROVED_BY as 'Approved By'
,LAB_RESULTS.update_count
FROM LAB_RESULTS
inner join patients on LAB_RESULTS.patient_no = Patients.Patient_No
inner join labtests on LabTests.TestId = LAB_RESULTS.TESTID
inner join Machines on Machines.Machine_id = LAB_RESULTS.machine_id
where LAB_RESULTS.APPROVED_BY is not null
and LAB_RESULTS.SAMPLE_STATUS = 6
and LAB_RESULTS.update_count in (select max(update_count) from LAB_RESULTS where LAB_RESULTS.SAMPLE_STATUS = 6 and LAB_RESULTS.deptid = 2 )
and LAB_RESULTS.deptid = 2
I expect to get the following result :
order_id test_id update_count
10 1 3
11 2 1
11 5 1
12 3 2
then i added this condition but this return only the maximum update count only not group by each order_id only the maximum for all orders.
and LAB_RESULTS.update_count in (select max(update_count) from LAB_RESULTS where LAB_RESULTS.SAMPLE_STATUS = 6 and LAB_RESULTS.deptid = 2 )
How can I do this ?
then i added this condition but this return only the maximum update
count only not group by each order_id only the maximum for all orders.
The only reason your update_count in() didn't work is because you didn't correlate the subquery.
Instead of this:
and LAB_RESULTS.update_count in (
select max(update_count)
from LAB_RESULTS
where LAB_RESULTS.SAMPLE_STATUS = 6
and LAB_RESULTS.deptid = 2
)
You need this:
and LAB_RESULTS.update_count in (
select max(update_count)
from LAB_RESULTS l2
where l2.SAMPLE_STATUS = 6
and l2.deptid = 2
AND l2.order_id=LAB_RESULTS.order_id --correlate to outer query
)
Although I recommend also aliasing the table in the main query and using both aliases in the subquery.
Add this into your select statement:
,DENSE_RANK() OVER
(PARTITION BY LAB_RESULTS.ORDER_ID ORDER BY LAB_RESULTS.update_count DESC) AS rank
Then you will wrap that result in a WITH clause and select and filter doing something like this:
WITH base_query as (
the current query you have with the extra column I suggested before) Select order_id, test_id, update_count where rank=1
CREATE TABLE t1
(order_id INT, test_id INT, update_count INT)
INSERT INTO t1 VALUES
(10,1,1),
(10,1,2),
(10,1,3),
(11,2,1),
(11,5,1),
(12,3,1),
(12,3,2)
SELECT * FROM dbo.t1
SELECT order_id, test_id, MAX(update_count)
FROM dbo.t1
GROUP BY order_id, test_id
ORDER BY order_id, test_id
DROP TABLE dbo.t1

Left outer join returning extra records

I have 2 tables namely "Item" and "Messages".
Item table has the columns like Id, Amount, etc.
Messages table has the columns like ItemId, Count, Comment, etc.
Here the common link between these 2 tables is the "Id" from Item and "ItemId" from Messages.
The "Count" column in the Messages table is just the count of comments per ItemId. i.e. When user updates the comment for any record, an entry gets created in the Messages table and Count for that particular ItemId shows as 1. If user updates one more comment to same record, the Count shows 2 and so on. If user does not update comment for a certain record, the entry does not get created in Messages table at all (NULL).
I want to capture all the records from the Item table irrespective of whether user has updated comment or not. If there are 0 comments, the query should return NULL in the Comments column for that record. But, If the user has updated the comment, it should pick up the comment having the highest "Count". E.g. if one record has 8 comments, the query should return only the record where Messages.Count=8 and not all 8 records. If only one comment, then that comment should be seen.
I have written LEFT OUTER JOIN but not able to get through as it shows all 8 records. In the results, I find 7 records with NULL as the count and the 8th record showing count as 8 but I need only this 8th record and not the other 7.
Any help would be highly appreciated. Below is my query:
Select
Id,
Amount,
Messages.Comment As Comments
From Item
Left Outer Join Messages ON Messages.ItemId=Item.Id
Left Outer Join (Select ItemId, MAX(Id) as max_id from Messages Group by ItemId) T ON Messages.ItemId=T.ItemId and Messages.Id=T.max_id
Where amount > 100
I've hooked up an example using temp tables which I think covers what you're looking for. Just remove the temp table stuff and replace with your actual tables and it should work.
CREATE TABLE #Item ( ID int PRIMARY KEY,
Amount numeric(9,2))
CREATE TABLE #Messages ( ItemId int REFERENCES #Item(ID),
[Count] smallint,
Comment nvarchar(max))
INSERT INTO #Item (ID, Amount)
SELECT 1, 100
UNION
SELECT 2, 120
UNION
SELECT 3, 140
UNION
SELECT 4, 50
INSERT INTO #Messages ( ItemID,
[Count],
Comment)
SELECT 1, 1, 'Comment 1 - 1'
UNION
SELECT 1, 2, 'Comment 1 - 2'
UNION
SELECT 2, 1, 'Comment 2 - 1'
UNION
SELECT 2, 1, 'Comment 3 - 1'
UNION
SELECT 2, 2, 'Comment 3 - 2'
SELECT I.Id,
I.Amount,
M.Comment
FROM #Item AS I
OUTER APPLY ( SELECT TOP 1 M.Comment
FROM #Messages AS M
WHERE M.ItemId = I.ID
ORDER BY M.[Count] DESC) AS M
WHERE i.amount > 100
DROP TABLE #Messages
DROP TABLE #Item
go for it bro....
Select
Id,
Amount,
T.Comment As Comments
From Item
Left Outer Join (Select ItemId, MAX(Id) as max_id, Comments from Messages Group by ItemId) T ON Item.ItemId=T.ItemId
Where amount > 100

Max Value with unique values in more than one column

I feel like I'm missing something really obvious here.
Using T-SQL/SQL-Server:
I have unique values in more than one column but want to select the max version based on one particular column.
Dataset:
Example
ID | Name| Version | Code
------------------------
1 | Car | 3 | NULL
1 | Car | 2 | 1000
1 | Car | 1 | 2000
Target status: I want my query to only select the row with the highest version value. Running a MAX on the version column pulls all three because of the distinct values in the 'Code' column:
SELECT ID
,Name
,MAX(Version)
,Code
FROM Table
GROUP BY ID, Name, Code
The net result is that I get all three entries as per the data set due to the unique values in the Code column, but I only want the top row (Version 3).
Any help would be appreciated.
You need to identify the row with the highest version as 1 query and use another outer query to pull out all the fields for that row. Like so:
SELECT t.ID, t.Name, GRP.Version, t.Code
FROM (
SELECT ID
,Name
,MAX(Version) as Version
FROM Table
GROUP BY ID, Name
) GRP
INNER JOIN Table t on GRP.ID = t.ID and GRP.Name = t.Name and GRP.Version = t.Version
You can also use row_number() to do this kind of logic, for example like this:
select ID, Name, Version, Code
from (
select *, row_number() over (order by Version desc) as RN
from Table1
) X where RN = 1
Example in SQL Fiddle
add the top statment to force the return of a single row. Also add the order by notation
SELECT top 1 ID
,Name
,MAX(Version)
,Code
FROM Table
GROUP BY ID, Name, Code
order by max(version) desc

SQL server logic for fetching data from the table

"Need to display all items linked to the parent category id=1 As per the table, It should fetch:Big Machine, Computer, CPU Cabinet, Hard Disk and Magnetic Disk. But by the logic that is written it is not fetching all the records. Plz help.."
create table ItemSpares
(
ItemName varchar(20),
ItemID int,
ParentCategoryID int
)
insert into ItemSpares (ItemName,ItemID,ParentCategoryID)
select 'Big Machine', 1 , NULL UNION ALL
select 'Computer', 2, 1 UNION ALL
select 'CPU Cabinet', 3, 2 UNION ALL
select 'Hard Disk', 4, 3 UNION ALL
select 'Magnetic Disk',5,4 UNION ALL
select 'Another Big Machine',6, NULL
You need to use a hierarchical SQL query, took a while to figure out but try this:
with BigComputerList (ItemName, ItemID, ParentCategoryID, Level)
AS
(
-- Anchor member definition
SELECT e.ItemName, e.ItemID, e.ParentCategoryID,
0 AS Level
FROM ItemSpares AS e
WHERE ItemID = 1
UNION ALL
-- Recursive member definition
SELECT e.ItemName, e.ItemID, e.ParentCategoryID,
Level + 1
FROM ItemSpares AS e
INNER JOIN BigComputerList AS d
ON e.ParentCategoryId = d.ItemID
)
Select * From BigComputerList
I would highly recommend reading this article if you want to understand what the query is doing.

SQL Server: Joining in rows via. comma separated field

I'm trying to extract some data from a third party system which uses an SQL Server database. The DB structure looks something like this:
Order
OrderID OrderNumber
1 OX101
2 OX102
OrderItem
OrderItemID OrderID OptionCodes
1 1 12,14,15
2 1 14
3 2 15
Option
OptionID Description
12 Batteries
14 Gift wrap
15 Case
[etc.]
What I want is one row per order item that includes a concatenated field with each option description. So something like this:
OrderItemID OrderNumber Options
1 OX101 Batteries\nGift Wrap\nCase
2 OX101 Gift Wrap
3 OX102 Case
Of course this is complicated by the fact that the options are a comma separated string field instead of a proper lookup table. So I need to split this up by comma in order to join in the options table, and then concat the result back into one field.
At first I tried creating a function which splits out the option data by comma and returns this as a table. Although I was able to join the result of this function with the options table, I wasn't able to pass the OptionCodes column to the function in the join, as it only seemed to work with declared variables or hard-coded values.
Can someone point me in the right direction?
I would use a splitting function (here's an example) to get individual values and keep them in a CTE. Then you can join the CTE to your table called "Option".
SELECT * INTO #Order
FROM (
SELECT 1 OrderID, 'OX101' OrderNumber UNION SELECT 2, 'OX102'
) X;
SELECT * INTO #OrderItem
FROM (
SELECT 1 OrderItemID, 1 OrderID, '12,14,15' OptionCodes
UNION
SELECT 2, 1, '14'
UNION
SELECT 3, 2, '15'
) X;
SELECT * INTO #Option
FROM (
SELECT 12 OptionID, 'Batteries' Description
UNION
SELECT 14, 'Gift Wrap'
UNION
SELECT 15, 'Case'
) X;
WITH N AS (
SELECT I.OrderID, I.OrderItemID, X.items OptionCode
FROM #OrderItem I CROSS APPLY dbo.Split(OptionCodes, ',') X
)
SELECT Q.OrderItemID, Q.OrderNumber,
CONVERT(NVarChar(1000), (
SELECT T.Description + ','
FROM N INNER JOIN #Option T ON N.OptionCode = T.OptionID
WHERE N.OrderItemID = Q.OrderItemID
FOR XML PATH(''))
) Options
FROM (
SELECT N.OrderItemID, O.OrderNumber
FROM #Order O INNER JOIN N ON O.OrderID = N.OrderID
GROUP BY N.OrderItemID, O.OrderNumber) Q
DROP TABLE #Order;
DROP TABLE #OrderItem;
DROP TABLE #Option;

Resources