SQL server logic for fetching data from the table - sql-server

"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.

Related

How to recreate old snapshot using field history table in Bigquery

I'm currently working on an interesting problem. I am trying to recreate state of table as it was on a given previous date. I have 2 tables
Table A: consists of live data, gets refreshed on an hourly basis.
Table A_field_history: consists of changes made to the fields in Table A.
Following image consists of current state, where Table A has live updated data and Table A_field_history only captures changes made to the fields on table A.
I am trying to recreate Table A as of particular given date. Following image consists of table state as it was on 06/30/2020.
The requirement is to have capability to recreate state of Table A based on any given date.
I actually identified a way to rollback (virtually, not on actual table) all the updates made after given specific date. Following are the steps followed:
Create dummy tables:
WITH
Table_A AS
(
SELECT 1 As ID, '2020-6-28' as created_date, 10 as qty, 100 as value
Union ALL
SELECT 2 As ID, '2020-5-29' as created_date, 20 as qty, 200 as value),
Table_A_field_history AS
(
SELECT 'xyz' id,'2020-07-29' created_date,'12345' created_by,'qty' field,'10' new_value,'200' old_value,'1' A_id
UNION ALL
SELECT 'abc' id,'2020-07-24' created_date,'12345' created_by,'qty' field,'20' new_value,'10' old_value,'2' A_id
UNION ALL
SELECT 'xyz' id,'2020-07-29' created_date,'12345' created_by,'value' field,'100' new_value,'2000' old_value,'1' A_id
UNION ALL
SELECT 'abc' id,'2020-07-24' created_date,'12345' created_by,'value' field,'200' new_value,'5000' old_value,'2' A_id
UNION ALL
SELECT 'xyz' id,'2020-06-29' created_date,'12345' created_by,'qty' field,'200' new_value,'' old_value,'1' A_id
UNION ALL
SELECT 'abc' id,'2020-05-30' created_date,'12345' created_by,'qty' field,'10' new_value,'' old_value,'2' A_id
UNION ALL
SELECT 'xyz' id,'2020-06-29' created_date,'12345' created_by,'value' field,'2000' new_value,'' old_value,'1' A_id
UNION ALL
SELECT 'abc' id,'2020-05-30' created_date,'12345' created_by,'value' field,'5000' new_value,'' old_value,'2' A_id
),
Step 1. Create date cte to filter data based on given date:
`date_spine
AS
(
SELECT * FROM UNNEST(GENERATE_DATE_ARRAY('2020-01-01', CURRENT_DATE(), INTERVAL 1 Day)) AS as_of_date
),`
Step 2. Above created date cte can be used as a Spine for our query, cross join to map as_of_date with all the changes made in the history table.
date_changes
AS
(
SELECT DISTINCT
date.as_of_date,
hist.A_id
FROM Table_A_field_history hist CROSS JOIN date_spine date
),
Step 3. Now we have as_of_date mapped to all historical transactions, now we can get max of change date.
most_recent_changes AS (
SELECT
dc.as_of_date,
dc.A_id ,
MAX(fh.created_date) AS created_date,
FROM date_changes dc
LEFT JOIN Table_A_field_history AS fh
ON dc.A_id = fh.A_id
WHERE CAST(fh.created_date AS DATE) <= dc.as_of_date
GROUP BY dc.as_of_date,
dc.A_id
),
Step 4. Now mapping max change date with actual created_date and history table
past_changes AS (
SELECT
mr.as_of_date,
mr.A_id,
mr.created_date,
a.id AS entry_id,
a.created_by AS created_by_id,
CASE WHEN a.field='qty' THEN a.new_value ELSE '' END AS qty,
CASE WHEN a.field='value' THEN a.new_value ELSE '' END AS value,
FROM most_recent_changes AS mr
LEFT JOIN Table_A_field_history AS a
ON mr.A_id = a.A_id
AND mr.created_date = a.created_date
WHERE a.id IS NOT NULL
)
Step 5. Now we can use as_of_date to get historical state of Table A.
Select *
From past_changes x
WHERE x.as_of_date = '2020-07-29'

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

structure this data so it can be read by a recursive cte? (SQL Server)

I need a query that will return the ancestry of a table given the information below. The source table is currently structured as described, and a recursive CTE doesn't work. I can't seem to wrap my mind around how the source table should be structured to make a CTE work.
Can someone please suggest a source table structure and query that will return the following? If there's a better way than recursion, that works too.
The source table contains information from my SQL Server database that reflects data lineage. In order to produce table T4, you'd execute procedures P1, P2, and P3.
There is a rule that a table can have only one 'parent' procedure, but a procedure can build multiple tables. So P3 can build T3 and T4, but T3 can only be built by one procedure (P3).
EXAMPLE:
If the query is fed 'T4', it should return this information:
referencing_ancestor referenced_ancestors
P3 T2, LOOKUP_TABLE
P2 T1
P1 staging
This is the source information in its current structure, but the structure can change. I just need the ancestry information of a given table.
declare #Dependencies table
(
id int identity(1,1),
referencing_name nvarchar(50) NOT NULL,
referenced_name nvarchar(50) NULL,
select_from int NULL,
insert_to int NULL
)
insert into #Dependencies
select 'P1', 'staging', 1, 0 --> P1 selects data from staging
union all
select 'P1', 'T1', 0, 1 --> P1 populates T1
union all
select 'P2', 'T1', 1, 0 --> P2 selects data from T1
union all
select 'P2', 'T2', 0, 1 --> P2 populates T2
union all
select 'P3', 'LOOKUP_TABLE', 1, 0 --> P3 selects data from LOOKUP_TABLE
union all
select 'P3', 'T2', 1, 0 --> P3 selects data from T2
union all
select 'P3', 'T3', 0, 1 --> P3 populates T3
union all
select 'P3', 'T4', 0, 1 --> P3 populates T4
This query doesn't work, not sure how to fix:
;with ancestors as
(
select referencing_name, referenced_name, Level = 0
from #Dependencies
where referenced_name = 'T4'
union all
select d.referencing_name, d.referenced_name, Level + 1
from #Dependencies d
inner join ancestors a on a.referenced_name = d.referenced_name
where insert_to = 0
)
select * from ancestors
I think you might want to establish some sort of "level" column in the #Dependencies table itself, but in this example I simply ordered by the referencing_name in the DENSE_RANK function.
;WITH ancestors AS (
SELECT *, DENSE_RANK() OVER (ORDER BY referencing_name) AS tbl_level
FROM #Dependencies
)
SELECT a2.*
FROM ancestors a1
JOIN ancestors a2 ON a2.tbl_level <= a1.tbl_level
WHERE a1.referenced_name = 'T4'
AND a2.insert_to = 0
ORDER BY tbl_level DESC

SQL Query based on occurrence of records

After a long time, I am getting a chance to post a SQL Server question here.
I have a table variable as shown below, in SQL Server 2005. This table is populated by a stored procedure written by some other team.
This is a order processing system. Each order can be accomplished by multiple processes by various departments, based on the OPRouteCode.
Taking example for OrderNo = 2, it has two OPRouteCode - but both these OPRouteCodes are using the same processes by same departments. They are considered equivalent OPRouteCodes.
On the other hand, for example OrderNo = 1, the processes and departments vary; hence they are not equivalent.
What is the best way to select only orders that has non-equivalent OPRouteCodes.
Note: If there is only one OPRouteCode, it is considered as equivalent only. Non-equivalence come only if there are more than one OPRouteCode.
What is the best SQL Server query to get this result? I couldn't write anything working after hours of effort.
DECLARE #OrderProcess TABLE (OrderNo Int,
OPRouteCode VARCHAR(5),
Department VARCHAR(10),
Process VARCHAR(20) )
--Order = 1 OPRouteCode = '0023'
INSERT INTO #OrderProcess
SELECT 1,'0023' ,'103','Receive'
UNION ALL
SELECT 1,'0023' ,'104','Produce'
UNION ALL
SELECT 1,'0023' ,'104','Pack'
UNION ALL
SELECT 1,'0023' ,'105','Ship'
--Order = 1 OPRouteCode = '0077'
INSERT INTO #OrderProcess
SELECT 1,'0077' ,'103','Receive'
UNION ALL
SELECT 1,'0077' ,'104','Produce'
UNION ALL
SELECT 1,'0077' ,'105','Ship'
--Order = 2 OPRouteCode = '0044'
INSERT INTO #OrderProcess
SELECT 2,'0044' ,'105','Receive'
UNION ALL
SELECT 2,'0044' ,'106','Ship'
--Order = 2 OPRouteCode = '0055'
INSERT INTO #OrderProcess
SELECT 2,'0055' ,'105','Receive'
UNION ALL
SELECT 2,'0055' ,'106','Ship'
Table Variable
Expected Output
Alright I got it this time. Sorry for the wrong answer before.
Select OrderNo,OPRouteCode
From (
select OrderNo,OPRouteCode, RANK() OVER(PARTITION BY OrderNo,Department ORDER BY Process ) 'Rnk'
from #OrderProcess
) a
Where Rnk =2

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