1

I have different product serial numbers in one table ProdHistory which contains, as the table name suggest, production history.
For example I have product serial SER001 which uses parts with its own serial number.
We also produce these parts thus uses the same table ProdHistory to track its subparts.
The same goes with the subparts and if it has sub-sub parts.

Sample Table

IF OBJECT_ID('tempDB.dbo.#SAMPLETable') IS NOT NULL DROP TABLE #SAMPLETable
CREATE TABLE #SAMPLETable
(
    ITEMSEQ INT IDENTITY(1,1),
    SERIAL NVARCHAR(10) COLLATE SQL_Latin1_General_CP850_CI_AS,
    ITEMID NVARCHAR(10) COLLATE SQL_Latin1_General_CP850_CI_AS,
    PARTSERIAL NVARCHAR(10) COLLATE SQL_Latin1_General_CP850_CI_AS,
    PARTID NVARCHAR(10) COLLATE SQL_Latin1_General_CP850_CI_AS,
    CREATEDDATETIME DATETIME
)

INSERT INTO 
       #SAMPLETable (SERIAL,ITEMID,PARTSERIAL,PARTID,CREATEDDATETIME) 
VALUES ('SER0001','ASY-1342','ITM0001','PRT-0808','2017-01-17'),
       ('SER0001','ASY-1342','ITM0002','PRT-0809','2017-01-17'),
       ('SER0001','ASY-1342','ITM0003','PRT-0810','2017-01-17'),
       ('SER0001','ASY-1342','ITM0004','PRT-0811','2017-01-17'),
       ('ITM0001','PRT-0808','UNT0001','PRT-2020','2017-01-16'),
       ('ITM0002','PRT-0809','UNT0002','PRT-2021','2017-01-16'),
       ('ITM0002','PRT-0809','UNT0003','PRT-2022','2017-01-16'),
       ('ITM0003','PRT-0810','UNT0004','PRT-2023','2017-01-16'),
       ('UNT0002','PRT-2021','DTA0000','PRT-1919','2017-01-15'),
       ('UNT0003','PRT-2022','DTA0001','PRT-1818','2017-01-15'),
       ('DTA0001','PRT-1818','LST0001','PRT-1717','2017-01-14')

The question is, if I'm given just the main serial number, how can I return all the parts and subparts serial associated with it?

Sample Result:

MainSerial SubSerial1 SubSerial2 SubSerial3 SubSerial4
-------------------------------------------------------
SER0001    ITM0001    UNT0001
SER0001    ITM0002    UNT0002    DTA0000
SER0001    ITM0002    UNT0003    DTA0001    LST0001
SER0001    ITM0003    UNT0004
SER0001    ITM0004   

In above, it is not definite how many parts and subparts there are for a serial number.
I did not post my code since what I'm doing right now is to query it one by one.
If I have known number of subparts, I can do nested Joins, however it is not.

Another question is, if I'm just given any of the subparts above, is it possible to return the same result?

L42
  • 19,427
  • 11
  • 44
  • 68
  • Could a part have more than 4 levels? If so, I would suggest handling the visualisation at the application level – JohnHC Jan 16 '17 at 10:05
  • 3
    Can you please post the table structure and sample data. It will allow us to provide you a meaningful query. – Abhay Chauhan Jan 16 '17 at 10:09
  • You will have to use CTE with filter in anchor query for the part/subpart for which you need to find the related subparts – Abhay Chauhan Jan 16 '17 at 10:10
  • Having dynamic number of columns in result needs Dynamic SQL ;). – shA.t Jan 16 '17 at 10:40
  • Help us help you → [How to post a T-SQL question on a public forum](http://spaghettidba.com/2015/04/24/how-to-post-a-t-sql-question-on-a-public-forum/) – TT. Jan 16 '17 at 10:52
  • Table structure and sample data plz? – Abdul Rasheed Jan 16 '17 at 10:53
  • Thanks for all your responses, I've added a sample table in its simple form. I post this in a rush and I not really looking for a ready made query but a pointer to a possible solution so never did add a sample table (my bad). – L42 Jan 16 '17 at 22:51

1 Answers1

1

I think a way is to use Dynamic SQL like this:

-- Variables to generate SQL query string dynamically
declare @cols nvarchar(max) = '', @joins nvarchar(max) = '', @sql nvarchar(max) = '';    

-- Using CTE to iterate parent-child records
with cte(i, cols, joins, itemId, serial, partId, partSerial) as (
    select 
        1, -- Level or depth of hierarchically tree 
        N's1.serial MainSerial, s1.partSerial SubSerial'+cast(1 as varchar(max)), 
        N'yourTable s'+cast(1 as varchar(max)), 
        s.itemId, s.serial, s.partId, s.partSerial
    from yourTable s
    -- A way to filter root-parents is filtering items those are not in parts
    where s.itemId not in (select si.partId from yourTable si)
    union all
    select 
        i+1, 
        cols + N', s'+cast(i+1 as varchar(max))+N'.partSerial SubSerial'+cast(i+1 as varchar(max)), 
        joins + N' left join yourTable s'+cast(i+1 as varchar(max))+N' on s'+cast(i as varchar(max))+N'.partId = s'+cast(i+1 as varchar(max))+N'.itemId', 
        st.itemId, st.serial, st.partId, st.partSerial
    from cte 
    join #sampleTable st on cte.partId = st.itemId
)
-- Now we need only strings of deepest level
select top(1) 
    @cols = cols, @joins = joins
from cte
order by i desc;

-- Finalize and executing query string
set @sql = N'select ' + @cols + N' from ' + @joins + N' where s1.itemId not in (select s.partId from yourTable s)';
exec(@sql);

Additional Note: Generated query is:

select s1.serial MainSerial
    , s1.partSerial SubSerial1
    , s2.partSerial SubSerial2
    , s3.partSerial SubSerial3
    , s4.partSerial SubSerial4 
  --, ...
from yourTable s1 
  left join yourTable s2 on s1.partId = s2.itemId 
  left join yourTable s3 on s2.partId = s3.itemId 
  left join yourTable s4 on s3.partId = s4.itemId 
--left join ...
where s1.itemId not in (select s.partId from yourTable s);
shA.t
  • 16,580
  • 5
  • 54
  • 111
  • Learned a lot from this. You're right, I have not explored dynamic query on SQL. This works great specially on the sample table. Working my way to make it work for multiple main serial numbers. Cheers. This is totally awesome mate. – L42 Jan 23 '17 at 03:06