Rearranging SQL Query Results - sql

I am working on an application that queries a SQL database to get Billing of Material information. I have successfully queried out the data that I wanted using the following query.
WITH bom (bomItem, partId, btmlvl)
AS
(
SELECT [bomItem], [partId], [btmlvl]
FROM [TESTDB].[dbo].[BOMTABLE]
WHERE [TESTDB].[dbo].[BOMTABLE].[bomItem] = 'PART# GOES HERE, PASSED IN BY THE APP'
UNION ALL
SELECT subQuery.bomItem, subQuery.partId, subQuery.btmlvl
FROM [TESTDB].[dbo].[BOMTABLE] AS subQuery
INNER JOIN bom AS mainQuery
on subQuery.bomItem = mainQuery.partId
)
SELECT [bomItem], [partId], [btmlvl]
FROM bom
The query results in (Sample Data):
bomItem partId btmlvl
---------------------------------
1 2 1
1 3 1
1 4 0
1 5 1
1 6 1
1 7 1
1 8 0
1 9 1
1 10 1
1 11 1
8 12 1
8 10 1
8 11 1
8 13 1
8 14 1
8 15 1
8 16 1
4 17 1
4 18 1
4 19 1
The data works as follows:
bomItem - The part number of the assembly that I am looking up the bill of materials for
partId - All of the parts and sub-assemblies tied to the bomItem I'm looking up
btmlvl - 0 indicates it's a sub-assembly, 1 indicates a standalone part (not really important, I was just using this to make sure I was getting the results that I wanted)
And what I want it to look like is (Expected Results):
bomItem partId btmlvl
---------------------------------
1 2 1
1 3 1
1 4 0
4 17 1
4 18 1
4 19 1
1 5 1
1 6 1
1 7 1
1 8 0
8 12 1
8 10 1
8 11 1
8 13 1
8 14 1
8 15 1
8 16 1
1 9 1
1 10 1
1 11 1
I could export it to a CSV or something else and write a script to rearrange the data, but I would prefer if it could be done as part of the SQL query. I messed around some with CASE statements but didn't quite achieve the desired results.
Bonus points if it could be aligned as follows (again the btmlvl isn't important, and will ultimately be removed). Again, novice SQL user here, but I think this would be possible using a PIVOT? (I just haven't gotten that far):
bomItem partId partId2
---------------------------------
...
1 8
8 12
8 10
8 11
8 13
8 14
8 15
8 16
...
Any help would be greatly appreciated, and please excuse the lengthiness of this post!
EDIT:
The only difference between my actual data and what I supplied is I simplified the part numbers from 11 characters strings to one-digit and two-digit integers and also removed other information I thought was irrelevant (e.g. qty on hand, purchasing cost, etc.). The first set of data is the sample data resulting from passing in "1" to WHERE [TESTDB].[dbo].[BOMTABLE].[bomItem] = 'PART# GOES HERE, PASSED IN BY THE APP'. This means that the first 10 lines of that data (again, anything with the bomItem == "1"), are all of the parts that make up bomItem == "1".
Looking at the "btmlvl" column for all bomItem == "1", you can see partId's 4 & 8 are assemblies (read sub-assemblies of bomItem == "1"), because btmlvl == "0". This means I need to perform additional queries to obtain the bill of materials for those part numbers. This is where the subQuery within the CTE comes in, giving me the line items starting with bomItem == "4" and bomItem == "8".
The difference between the output as it stands right now, and the output that is desired is the current output lists all parts for bomItem == "1", then all parts for bomItem == "8", and lastly all parts for bomItem == "4".
But what I want is where bomItem == "1" & partId's 1 through 3 to appear as they are right now, but once partId == "4", directly underneath that line I want all of the lines where bomItem == "4", then continue with the lines that contain bomItem == "1" & partId's 5, 6, 7, but again once partId 8 is reached, do a similar thing as with partId 4, then finish partId's 9, 10, 11 as normal.

This is inherently hierarchal data, so you need a self-join.
I think this gets you what you want.
SELECT a.* FROM bom a
LEFT JOIN bom b
ON a.bomitem = b.partID
ORDER BY ISNULL (b.partID, a.partID), a.partID

Related

PrestoDB - Allocate a value across multiple rows in presto

I have a dataset where in I need to allocate a value across multiple rows. Below is an example.
id pending_cnt date available_count
1 10 12/5/2022 5
1 10 12/6/2022 3
In this example the pending_cnt has to be allocated among the first 2 rows based on descending order of date and generate 3 new columns(available/not available/status) and a new row has to be inserted with a missing allocation
Expected output
id pending_cnt date available_count available not_available status
1 10 12/6/2022 5 5(out of 10, 5 is available, remaining 5) 0 AVAILABLE
1 10 12/5/2022 3. 3(out of remaining 5, 3 is available, remaining 2) 0 AVAILABLE
1 10 NULL 0 0 2 NOT AVAILABLE
Here is another example
id pending_cnt date available_count
1 10 12/6/2022 5
1 10 12/5/2022 5
Expected output is just 2 rows
id pending_cnt date available_count available not_available STATUS
1 10 12/6/2022 5 5 0 AVAILABLE
1 10 12/5/2022 5 5 0 AVAILABLE
Any help would be appreciated.
Thanks in advance

Double Join to get data from a table based on other two tables

I have three tables with the following key fields:
CONTRACTS
reference
package
EVENTS
reference
condition1
condition2
TRADES
reference
event_reference
Basically, what I would like to do is the following:
Get all the reference of the table EVENTS where the two conditions (condition1 and condition2) are met;
Hence, getting all the reference of the table TRADES where TRADES.event_reference = EVENTS.reference
Finally, getting the CONTRACTS.package where the CONTRACTS.reference = TRADES.reference (after having filtered the data at the point 2).
In order to do this, I have tried a JOIN statement:
SELECT CONTRACTS.package
FROM CONTRACTS
JOIN TRADES ON CONTRACTS.reference = TRADES.reference
JOIN EVENTS ON TRADES.event_reference = EVENTS.reference
WHERE EVENTS.condition1 = '1.511' AND EVENTS.condition2 IN (1,2)
However, the above (which is executed without errors) does not issue any result, and I would actually expect to see some.
I hence understand that I'm being wrong in the logic that I follow: could anyone please help?
EDIT: this is an example of how the data look like (in yellow, I have highlighted the data that would be touched in the query if it was working as I had it in mind:
...here is the expected result:
1 (package of 4, related to 11 which satisfies condition 1 and 2)
2 (package of 6, related to 13 which satisfies condition 1 and 2)
4 (package of 10, related to 16 which satisfies condition 1 and 2)
and here are the data to copy-paste them:
CONTRACTS
reference package
1 1
2 1
3 1
4 1
5 2
6 2
7 3
8 3
9 4
10 4
EVENTS
reference condition1 condition2
10 1.511 0
11 1.511 1
12 1.202 0
13 1.511 2
14 1.511 0
15 1.202 0
16 1.511 1
TRADES
reference event_reference
2 10
4 11
5 12
6 13
7 14
9 15
10 16
Your query looks OK
SQL Fiddle Demo
SELECT CONTRACTS.package
FROM CONTRACTS
JOIN TRADES ON CONTRACTS.reference = TRADES.reference
JOIN EVENTS ON TRADES.event_reference = EVENTS.reference
WHERE EVENTS.condition1 = 'true' AND EVENTS.condition2 = 'true'
OUTPUT
| package |
|---------|
| 1 |
| 2 |
| 4 |

Using temporary extended table to make a sum

From a given table I want to be able to sum values having the same number (should be easy, right?)
Problem: A given value can be assigned from 2 to n consecutive numbers.
For some reasons this information is stored in a single row describing the value, the starting number and the ending number as below.
TABLE A
id | starting_number | ending_number | value
----+-----------------+---------------+-------
1 2 5 8
2 0 3 5
3 4 6 6
4 7 8 10
For instance the first row means:
value '8' is assigned to numbers: 2, 3 and 4 (5 is excluded)
So, I would like the following intermediairy result table
TABLE B
id | number | value
----+--------+-------
1 2 8
1 3 8
1 4 8
2 0 5
2 1 5
2 2 5
3 4 6
3 5 6
4 7 10
So I can sum 'value' for elements having identical 'number'
SELECT number, sum(value)
FROM B
GROUP BY number
TABLE C
number | sum(value)
--------+------------
2 13
3 8
4 14
0 5
1 5
5 6
7 10
I don't know how to do this and didn't find any answer on the web (maybe not looking with appropriate key words...)
Any idea?
You can do what you want with generate_series(). So, TableB is basically:
select id, generate_series(starting_number, ending_number - 1, 1) as n, value
from tableA;
Your aggregation is then:
select n, sum(value)
from (select id, generate_series(starting_number, ending_number - 1, 1) as n, value
from tableA
) a
group by n;

MDX: iif condition on the value of dimension

I have 1 Virtual cube consists of 2 cubes.
Example of fact table of 1st cube.
id object_id time_id date_id state
1 10 2 1 0
2 11 5 1 0
3 10 7 1 1
4 10 3 1 0
5 11 4 1 0
6 11 7 1 1
7 10 8 1 0
8 11 5 1 0
9 10 7 1 1
10 10 9 1 2
Where State: 0 - Ok, 1 - Down, 2 - Unknown
For this cube I have one measure StateCount it should count States for each object_id.
Here for example we have such result:
for 10 : 3 times Ok , 2 times Down, 1 time Unknown
for 11 : 3 times Ok , 1 time Down
Second cube looks like this:
id object_id time_id date_id status
1 10 2 1 0
2 11 5 1 0
3 10 7 1 1
4 10 3 1 1
5 11 4 1 1
Where Status: 0 - out, 1 - in. I keep this in StatusDim.
In this table I keep records that should not be count. If object have status 1 that means that I have exclude it from count.
If we intersect these tables and use StateCount we will receive this result:
for 10 : 2 times Ok , 1 times Down, 1 time Unknown
for 11 : 2 times Ok , 1 time Down
As far as i know, i must use calculated member with IIF condition. Currently I'm trying something like this.
WITH MEMBER [Measures].[StateTimeCountDown] AS(
iif(
[StatusDimDown.DowntimeHierarchy].[DowntimeStatus].CurrentMember.MemberValue
<> "in"
, [Measures].[StateTimeCount]
, null )
)
The multidimensional way to do this would be to make attributes from your state and status columns (hopefully with user understandable members, i. e. using "Ok" and not "0"). Then, you can just use a normal count measure on the fact tables, and slice by these attributes. No need for complex calculation definitions.

TSQL -- Retrieve parent ID

The raw data in my table consists of an ID column and a column 'IsFolder' which can be 1 or 0, like this:
ID IsFolder
1 1
2 0
3 0
4 1
5 0
6 0
7 0
8 0
9 0
10 0
11 1
12 0
13 0
14 0
15 1
16 0
17 0
The way the code interprets this is that if a line has a 1 for 'IsFolder' then it is a Folder, and all of the tasks below it (until you hit the next Folder) are children of that folder.
What I'd like to have is a Select statement that will just return the ID of the parent folder for all non-folder tasks. So something like:
ID ParentFolder
2 1
3 1
5 4
6 4
7 4
8 4
9 4
10 4
12 11
13 11
14 11
16 15
17 15
I'm using MS SQL Server Management Studio 2005. I feel like this is an easy answer to those familiar with using cursors (which I am not). I can't think of any other way to do it but maybe someone else can. Anyway thanks in advance for any answers and sorry if I did something wrong, this is my first post.
You don't need a cursor for that - just a subquery:
SELECT ID,
(SELECT MAX(ID)
FROM Folders
WHERE ID < f.ID
AND IsFolder = 1) AS Parent
FROM Folders f
WHERE IsFolder = 0
I'd just like to point out that this task would be much easier, and the overall design much more extensible, if the structure of the data was changed a little. How are you going to add a task to folder 4, for example? If the parent and child relationship was extracted to two different tables, it might help.