Parent and Part Relationship Cost Calculation using SQL - sql

I have a Child and Parent relationship table with cost.
I need to roll up the child part costs into parent.
Attached is the image for the table.
Reqd. column shows the cost rollup I need.
But I am not able to do it using sql queries.
Also, recursive queries won't help since total line items are more than 100k.
Any suggestion or ideas will be very helpful.

I don't understand you are saying you can't use CTE & SQL query. So what technical base do you want to rely on?
By SQL it is totally doable with the functions of aggregate & joins
EDIT:
Without your schema, try to adapt this query for your table :
SELECT j.ID, j.Parent_ID, SUM(t.Cost)
FROM MyTableCost as t
INNER join MyTableCost AS j on j.ID = t.Parent_ID
GROUP BY j.ID, j.Parent_ID;

One self-join seems to be enough.
But I don't see the logic for rolling up id 4.
select t0.parent_id, t0.part_id, t0.cost
, coalesce(sum(t1.cost), t0.cost) as reqd_cost
from your_weird_hierarchy_table t0
left join your_weird_hierarchy_table t1
on t1.parent_id = t0.part_id
group by t0.parent_id, t0.part_id, t0.cost;
parent_id | part_id | cost | reqd_cost
--------: | ------: | -----: | --------:
1 | 2 | 1.0000 | 4.0000
1 | 3 | 2.0000 | 9.0000
1 | 4 | 1.0000 | 1.0000
2 | 3 | 3.0000 | 9.0000
2 | 4 | 1.0000 | 1.0000
3 | 4 | 5.0000 | 5.0000
3 | 5 | 3.0000 | 3.0000
3 | 6 | 1.0000 | 1.0000
db<>fiddle here

Related

Merging some columns from two postgres tables into a new table based on row value

Hello PostgresSQL experts (and maybe this is also a task for Perl's DBI since I also happen to be working with it, but...) I might also have some terminologies misused here so bear with me.
I have a set of 32 tables, every one exactly as the other. The first column of every table always contains a date, while the second column contains values (integers) that can change once every 24 hours, some samples get back-dated. In many cases, the tables may not contain data for a particular date, ever. So here's an example of two such tables:
date_list | sum date_list | sum
---------------------- --------------------------
2020-03-12 | 4 2020-03-09 | 1
2020-03-14 | 5 2020-03-11 | 3
| 2020-03-12 | 5
| 2020-03-13 | 9
| 2020-03-14 | 12
The idea is to merge the separate tables into one, sort of like a grid, but with the samples placed in the correct row in its own column and ensuring that the date column (always the first column) is not missing any dates, looking like this:
date_list | sum1 | sum2 | sum3 .... | sum32
---------------------------------------------------------
2020-03-08 | | |
2020-03-09 | | 1 |
2020-03-10 | | | 5
2020-03-11 | | 3 | 25
2020-03-12 | 4 | 5 | 35
2020-03-13 | | 9 | 37
2020-03-14 | 5 | 12 | 40
And so on, so 33 columns by 2020-01-01 to date.
Now, I have tried doing a FULL OUTER JOIN and it succeeds. It's the subsequent attempts that get me trouble, creating a long, cascading table with the values in the wrong place or accidentally clobbering data. So I know this works if I use a table of one column with a date sequence and joining the first data table, just as a test of my theory using baby steps:
SELECT date_table.date_list, sums_1.sum FROM date_table FULL OUTER JOIN sums_1 ON date_table.date_list = sums_1.date_list
2020-03-07 | 1
2020-03-08 |
2020-03-09 |
2020-03-10 | 2
2020-03-11 |
2020-03-12 | 4
Encouraged, I thought I'd get a little more ambitious with my testing, but that places some rows out of sequence to the bottom of the table and I'm not sure that I'm losing data or not, this time trying USING as an alternative:
SELECT * FROM sums_1 FULL OUTER JOIN sums_2 USING (date_list);
Result:
fecha_sintomas | sum | sum
----------------+-------+-------
2020-03-09 | | 1
2020-03-11 | | 3
2020-03-12 | 4 | 5
2020-03-13 | | 9
2020-03-14 | 5 | 12
2020-03-15 | 6 | 15
2020-03-16 | 8 | 20
: : :
2020-10-29 | 10053 | 22403
2020-10-30 | 10066 | 22407
2020-10-31 | 10074 | 22416
2020-11-01 | 10076 | 22432
2020-11-02 | 10077 | 22434
2020-03-07 | 1 |
2020-03-10 | 2 |
(240 rows)
I think I'm getting close. In any case, where do I get to what I want, which is my grid of data described above? Maybe this is an iterative process that could benefit from using DBI?
Thanks,
You can full join like so:
select date_list, s1.sum as sum1, s2.sum as sum2, s3.sum as sum3
from sums_1 s1
full join sums_2 s2 using (date_list)
full join sums_3 s3 using (date_list)
order by date_list;
The using syntax makes unqualified column date_list unambiguous in the select and order by clauses. Then, we need to enumerate the sum columns, provided aliases for each of them.

Joining Two SQL Tables on Different Databases [duplicate]

This question already has answers here:
Can we use join for two different database tables?
(2 answers)
Closed 4 years ago.
I have two tables on two separate databases. We will call them Table1 and Table2.
Table1:
+----------+----------+----------+---------+------+----------+
| UniqueID | Date1 | Date2 | Fruit | Cost | Duration |
+----------+----------+----------+---------+------+----------+
| 1 | 09/10/18 | 10/20/18 | Apples | 1.50 | 7 |
| 2 | 09/18/18 | 10/25/18 | Oranges | 1.75 | 10 |
| 3 | 10/01/18 | 10/30/18 | Bananas | 2.00 | 10 |
+----------+----------+----------+---------+------+----------+
Table2:
+----------+---------+------+----------+-----------+
| Date1 | Fruit | Cost | Duration | New Price |
+----------+---------+------+----------+-----------+
| 09/10/18 | Savory | 1.50 | 7 | 1.90 |
| 09/18/18 | Citrusy | 1.75 | 10 | 2.50 |
| 10/01/18 | Mealy | 2.00 | 10 | 2.99 |
| 10/20/18 | Savory | 1.50 | 7 | 3.90 |
| 10/25/18 | Citrusy | 1.75 | 10 | 4.50 |
| 10/30/18 | Mealy | 2.00 | 10 | 5.99 |
+----------+---------+------+----------+-----------+
What I need the output to look like:
+----------+----------+--------------------+----------+--------------------+
| UniqueID | Date1 | New Price on Date1 | Date2 | New Price on Date2 |
+----------+----------+--------------------+----------+--------------------+
| 1 | 09/10/18 | 1.90 | 10/20/18 | 3.90 |
| 2 | 09/18/18 | 2.50 | 10/25/18 | 4.50 |
| 3 | 10/01/18 | 2.99 | 10/30/18 | 5.99 |
+----------+----------+--------------------+----------+--------------------+
I need to first convert table1.fruit to the representation of table2.fruit (apples-->savory, oranges-->citrusy, bananas-->mealy) then join on table1.fruit = table2.fruit, table1.duration = table2.duration, table1.cost = table2.cost, table1.date1 = table2.date1, and table1.date2 = table2.date1.
I don't know where to start on writing the statement. I looked over some previous questions posted here, but they really just go over the basics of linking two tables from different databases. Do I convert the table1.fruit first in the select statement, then join, or do I convert table1.fruit in the join statement? How do I join table2.date1 on both table1.date1 and table1.date2 to get the price associated with both dates?
If I can provide any more information for you, please let me know.
I am on SQL Server 2017 using Management Studio.
Thanks for any help in advance!
Create a mapping table to bridge between the different codes for fruits.
IF OBJECT_ID('tempdb..#FruitMappings') IS NOT NULL
DROP TABLE #FruitMappings
CREATE TABLE #FruitMappings (
Table1Fruit VARCHAR(100),
Table2Fruit VARCHAR(100))
INSERT INTO #FruitMappings (
Table1Fruit,
Table2Fruit)
VALUES
('Apples', 'Savory'),
('Oranges', 'Citrusy'),
('Bananas', 'Mealy')
SELECT
T1.*
--, whichever columns you need
FROM
Database1.Schema1.Table1 AS T1
INNER JOIN #FruitMappings AS F ON T1.Fruit = F.Table1Fruit
INNER JOIN Database2.Schema2.Table2 AS T2 ON
F.Table2Fruit = T2.Fruit AND
T1.Cost = T2.Cost AND
T1.Duration = T2.Duration
-- AND any other matches you need
You can use LEFT JOIN or even FULL JOIN, depending if you might have some fruits on a table that aren't available on the other (careful with NULL values if FULL JOIN).
If both databases are on the same SQL Server instance and your SQL Server login has access to both databases you can just use the full form of the object names:
select * -- Whatever...
from Database1.dbo.Table1 t1
inner join Database2.dbo.Table2 t2
on t1,UniqueId = t2.UniqueId -- Or whatever your join condition is
(adding where etc. clauses as required.)
This assumes both databases are using the default schema, otherwise replace dbo as necessary.
If the databases are on different servers you can use linked servers, but there are performance implications (the whole remote table may be read because the optimiser can't do much to filter it).

SQL query using (self?) Join

I have a problem with a question I don't fully understand. Was wondering if anyone could help me with it, Or at least help me understand.
Lets say we have a table 'Jobs' with 2 columns job numbers 'jobnum', and employee numbers 'empnum'
Table: Jobs
---------------------------------------
| jobnum | empnum |
---------------------------------------
| 125 | 4785 |
| 100 | 4200 |
| 305 | 4001 |
| 125 | 4224 |
| 102 | 4840 |
| 100 | 4224 |
| 107 | 4534 |
| 255 | 4200 |
| 208 | 4224 |
| 301 | 4785 |
---------------------------------------
I like the job that was done at a certain work site, lets take jobnum '125', and want to know the other jobnums of the same employees. It shows that two people worked on jobnum 125. '4224' and '4785'. How would I write a SQL query that would output the jobnums of the same people that did job '125'. I am supposed to use Join query and cannot use sub-query
I understand how I would do it using a sub-query but don't know how I would going about it using a Join. I am assuming I would do a self join? Perhaps I don't fully understand Joins =/
Using Join:
Select
j1.jobnum,
j1.empnum
From jobs j1
Join jobs j2
On j1.empnum = j2.empnum
Where j2.jobnum = 125
The query says "Give me all the records from jobs (j1) that have the same empnum as any record in jobs (j2) which has a jobnum of 125. That's the self-join that you're looking for.
This is a generic solution
SELECT T1.*
JOBS T1
INNER JOIN JOBS T2
ON( T1.jobnum=T2.jobnum AND T1.empnum<> T2.empnum)

How to Transpose a resultset from SQL

I am using Microsoft SQL Server 2008.
I have a table that looks something like this:
|======================================================|
| RespondentId | QuestionId | AnswerValue | ColumnName |
|======================================================|
| P123 | 1 | Y | CanBathe |
|------------------------------------------------------|
| P123 | 2 | 3 | TimesADay |
|------------------------------------------------------|
| P123 | 3 | 1.00 | SoapPrice |
|------------------------------------------------------|
| P465 | 1 | Y | CanBathe |
|------------------------------------------------------|
| P465 | 2 | 1 | TimesADay |
|------------------------------------------------------|
| P465 | 3 | 0.99 | SoapPrice |
|------------------------------------------------------|
| P901 | 1 | N | CanBathe |
|------------------------------------------------------|
| P901 | 2 | 0 | TimesADay |
|------------------------------------------------------|
| P901 | 3 | 0.00 | SoapPrice |
|------------------------------------------------------|
I would like to flip the rows to be columns so that this table looks like this:
|=================================================|
| RespondentId | CanBathe | TimesADay | SoapPrice |
|=================================================|
| P123 | Y | 3 | 1.00 |
|-------------------------------------------------|
| P465 | Y | 1 | 0.99 |
|-------------------------------------------------|
| P901 | N | 0 | 0.00 |
|-------------------------------------------------|
(the example data here is arbitrarily made up, so its silly)
The source table is a temp table with approximately 70,000 rows.
What SQL would I need to write to do this?
Update
I don't even know if PIVOT is the right way to go.
I don't know what column to PIVOT on.
The documentation mentions <aggregation function> and <column being aggregated> and I don't want to aggregate anything.
Thanks in advance.
It, is required to use an aggregate function if you use PIVOT. However, since your (RespondentId, QuestionId) combination is unique, your "groups" will have only one row, so you can use MIN() as an aggregate function:
SELECT RespondentId, CanBathe, TimesADay, SoapPrice
FROM (SELECT RespondentId, ColumnName, AnswerValue FROM MyTable) AS src
PIVOT (MIN(AnswerValue) FOR ColumnName IN(CanBathe, TimesADay, SoapPrice)) AS pvt
If a group only contain one row, then MIN(value) = value, or in other words: the aggregate function becomes the identity function.
See if this gets you started. Used to have to use CASE statements to make that happen but it looks like some inkling of PIVOT is in SQL Server now.
PIVOT is a start, but the thing with sql queries is that you really need to know what columns to expect in the result set before writing the query. If you don't know this, the last time I checked you have to either resort to dynamic sql or allow the client app that retrieves the data to do the pivot instead.

Merge computed data from two tables back into one of them

I have the following situation (as a reduced example). Two tables, Measures1 and Measures2, each of which store an ID, a Weight in grams, and optionally a Volume in fluid onces. (In reality, Measures1 has a good deal of other data that is irrelevant here)
Contents of Measures1:
+----+----------+--------+
| ID | Weight | Volume |
+----+----------+--------+
| 1 | 100.0000 | NULL |
| 2 | 200.0000 | NULL |
| 3 | 150.0000 | NULL |
| 4 | 325.0000 | NULL |
+----+----------+--------+
Contents of Measures2:
+----+----------+----------+
| ID | Weight | Volume |
+----+----------+----------+
| 1 | 75.0000 | 10.0000 |
| 2 | 400.0000 | 64.0000 |
| 3 | 100.0000 | 22.0000 |
| 4 | 500.0000 | 100.0000 |
+----+----------+----------+
These tables describe equivalent weights and volumes of a substance. E.g. 10 fluid ounces of substance 1 weighs 75 grams. The IDs are related: ID 1 in Measures1 is the same substance as ID 1 in Measures2.
What I want to do is fill in the NULL volumes in Measures1 using the information in Measures2, but keeping the weights from Measures1 (then, ultimately, I can drop the Measures2 table, as it will be redundant). For the sake of simplicity, assume that all volumes in Measures1 are NULL and all volumes in Measures2 are not.
I can compute the volumes I want to fill in with the following query:
SELECT Measures1.ID, Measures1.Weight,
(Measures2.Volume * (Measures1.Weight / Measures2.Weight))
AS DesiredVolume
FROM Measures1 JOIN Measures2 ON Measures1.ID = Measures2.ID;
Producing:
+----+----------+-----------------+
| ID | Weight | DesiredVolume |
+----+----------+-----------------+
| 4 | 325.0000 | 65.000000000000 |
| 3 | 150.0000 | 33.000000000000 |
| 2 | 200.0000 | 32.000000000000 |
| 1 | 100.0000 | 13.333333333333 |
+----+----------+-----------------+
But I am at a loss for how to actually insert these computed values into the Measures1 table.
Preferably, I would like to be able to do it with a single query, rather than writing a script or stored procedure that iterates through every ID in Measures1. But even then I am worried that this might not be possible because the MySQL documentation says that you can't use a table in an UPDATE query and a SELECT subquery at the same time, and I think any solution would need to do that.
I know that one workaround might be to create a new table with the results of the above query (also selecting all of the other non-Volume fields in Measures1) and then drop both tables and replace Measures1 with the newly-created table, but I was wondering if there was any better way to do it that I am missing.
UPDATE Measures1
SET Volume = (Measures2.Volume * (Measures1.Weight / Measures2.Weight))
FROM Measures1 JOIN Measures2
ON Measures1.ID = Measures2.ID;