SQL query to combine column data from multiple columns - sql

SQL Server 2012, Python 3.
Background Info:
I have two tables; LOADS and FACTORS.
LOADS has the following columns:
FACTORS has the following columns:
Table FACTORS is basically a roadmap how to combine and factor the data in table LOADS.
FACTORS columns mlc and tlc contain the same numbers as LOADS table column subcase.
In this example we will only use value 32771 for LOADS column eid.
Problem:
I need to get a set of results with columns similar to LOADS but with additional columns, like this:
eid, mlc, tlc, fx, fy, fz.
To do this manually:
start with the first row of FACTORS. mlc=1053002, tlc=4053400
select fx, fy, fz from LOADS where subcase=mlc and eid=32771 (this is our mlc loads)
select fx, fy, fz from LOADS where subcase=tlc and eid=32771 (this is our tlc loads)
the final values in columns fx, fy, fz are from these equation:
(mlc.fx * mf + tlc.fx * tf) * cf
(mlc.fy * mf + tlc.fy * tf) * cf
(mlc.fz * mf + tlc.fz * tf) * cf
If tlc=0 then only use mlc. The fx, fy, fz values are from these equations:
(mlc.fx * mf) * cf
(mlc.fy * mf) * cf
(mlc.fz * mf) * cf
I am somewhere between beginner and intermediate using SQL so I have no idea how to do this using only SQL. I have successfully done this using pandas basically doing this the manual way by creating a blank DataFrame, calculating the (3) DOF fx,fy,fz and adding Rows one at a time, building the DataFrame from the ground up. I can share that code if anyone needs to see it, but I really want to do this in SQL if it's possible. The reason is because most general queries using this procedure can take several minutes (for 50,000+ Rows, granted my pandas knowledge might not be too efficient for real world usage) and I really want to get that time down to a few seconds.

Following ZLK's comment, it seems like you want this:
select
LoadsMlc.eid,
Factors.mlc,
Factors.tlc,
(LoadsMlc.fx * Factors.mf + isnull(LoadsTlc.fx * Factors.tf, 0)) * Factors.cf fx,
(LoadsMlc.fy * Factors.mf + isnull(LoadsTlc.fy * Factors.tf, 0)) * Factors.cf fy,
(LoadsMlc.fz * Factors.mf + isnull(LoadsTlc.fz * Factors.tf, 0)) * Factors.cf fz
from
dbo.Factors
join dbo.Loads LoadsMlc on Factors.mlc = LoadsMlc.subcase
left outer join dbo.Loads LoadsTlc on nullif(Factors.tlc, 0) = LoadsTlc.subcase
This assumes there exists exactly one match for subcase against mlc--your question doesn't currently indicate one way or another. You may want to, perhaps, add something like
where
LoadsTlc.eid is null -- We didn't find a tlc (it was 0)
or LoadsMlc.eid = LoadsTlc.eid
If there are potentially multiple matches across the Loads table for each of the Factors. Note that the tlc join is a left outer, as you indicated that we should only use mlc in the case that it is 0.
The second join to Loads uses a nullif to nullify the case when tlc is 0. In that case you'll receive no records for that value. The three columns account for this null case by adding 0.

Related

Multiply values ​of two variables according to id

I have a problem with the multiplication of two variables.
I have two sql statements with which I get the number of hours that an employee has worked during a specific month, and another statement to get the hourly cost that each employee has.
I must say that they are two different tables in the database.
What I would like is to multiply the hours of each employee by the hourly cost that employee has, and then add the results.
These two sentences work fine, if I do "print_r" I see the results that each employee has.
I guess I have to do it through the ID, but I don't know how to do it.
This is what I have so far, and I only get it to multiply the first person it finds.
$SumaHoras = $DB->Sql ("SELECT Id_Persona, CONCAT(SUM(Horas)* 3600)/36000000 as Hora FROM table1");
$SumaCosteHora = $DB->Sql("SELECT Id_CostPers, CONCAT(Coste_Hora) as Coste FROM table2");
$suma1 = [];
while($totalSumaHoras = $DB->Row($SumaHoras)){
$suma1 = $totalSumaHoras;
print_r($suma1);
}
$suma2 = [];
while($totalSumaCoste = $DB->Row($SumaCosteHora)){
$suma2 = $totalSumaCoste;
print_r($suma2);
}
$total4 = $suma1->Hora * $suma2->Coste;
I don't know if I explained myself well, but I appreciate any help you can offer me.
Your first line of code does not work - it generates errors - "The concat function requires 2 to 254 arguments." and then if you remove the CONCAT you get "Column 'table1.Id_Persona' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause." - as pointed out by #isamu
You should not use CONCAT for numbers - that returns a string - always use the correct Type for data.
To get all the information you require at once you need to use a JOIN see This article
E.g.
SELECT Id_Persona, t2.Coste_Hora * (SUM(t1.Horas) * 3600.0)/36000000.0
FROM table1 t1
INNER JOIN table2 t2 ON t1.Id_Persona = t2.Id_CostPers
group by Id_Persona, t2.Coste_Hora
I'm not sure why you are using that particular calculation but note I have appended .0 to those integer values to ensure that decimal calculation takes place - to explain what I mean try running these two lines of SQL
SELECT 10 * 3600/36000000;
SELECT 10 * 3600.0/36000000.0;

Two Tables/Databases error: Only one expression can be specified in the select list when the subquery is not introduced with EXISTS

Using SQL Server I'm trying to multiply two columns of a distinct part number using 2 tables and 2 databases but it gives this error:
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
This SQL joins two tables in different databases and show the Final Part Number (FinalPartNo) for a bumper and the ancillary parts that it needs to put it together (bolts, brackets etc.)
Query:
SELECT
tb_1.FinalPartNo,
tb_1.SubPartType,
tb_1.SubPart,
tb_1.FinalItemSubPartQuantity,
tb_2.PurchasedOrMfg,
tb_2.SalesWeek1,
tb_2.SalesWeek2
FROM [009Reports].[dbo].[ANC Parts] tb_1
JOIN [555].[cache].[PurchasingSupplyChainNeeds] tb_2 ON tb_1.FinalPartNo = tb_2.ItemNo
So it displays this:
If you look at the table and to siplify things, I highlighted 3 part numbers that all use the same SubPart. Two of them use 4 of the FinalSubPartQuantity and one uses 2 in the "install" step. For SalesWeek1 of the highlighted in the image, they sold two of the FinalPartNo which requires 4 FinalSubPartQuantity and had two sales so that totals 8 needed for that week. I don't need the FinalPartNo but added that to show that it's multiple FinalPartNo with the same subpart.
Trying to figure to sum them up with a totals column for each SubPart for that week (for 52 weeks, just showing 2).
In this example, 03CSFY-0500350 for SalesWeek1 could total 150 having it on
multiple FinalPartNo and multiple steps (Fabricate, Assembly, Install).
So, I tried a subquery to make the SubPart distinct and multiply the FinalSubPartQuantity x SalesWeek1 for TotalSalesWeek1 but getting error. Trying to figure out syntax.
SELECT
tb_1.SubPart,
tb_1.FinalItemSubPartQuantity,
TotalSalesWeek1 = (SELECT DISTINCT(tb_1.SubPart),
tb_1.FinalItemSubPartQuantity * tb_2.SalesWeek1),
TotalSalesWeek2 = (SELECT DISTINCT(tb_1.SubPart),
tb_1.FinalItemSubPartQuantity * tb_2.SalesWeek2)
FROM [009Reports].[dbo].[ANC Parts] tb_1
JOIN [555].[cache].[PurchasingSupplyChainNeeds] tb_2 ON tb_1.FinalPartNo = tb_2.ItemNo
I'm just trying to display:
SubPart/FinalSubPartQuantity/TotalSalesWk1/TotalSalesWk2/TotalSalesWk3/ to week 52. So it just shows the sub part, sum of all the FinalSubPartQuantity amounts for that part for all the different FinalPartNo's and the total sales FinalItemPartQuantity * SalesWeek1, 2, 3...
summarize: subpart and how many sold of that part that week.
You can't set the TotalSalesWeek1 to two columns (DISTINCT(tb_1.SubPart) and tb_1.FinalItemSubPartQuantity * tb_2.SalesWeek1).
I would suggest something like the following
SELECT
tb_1.SubPart,
SUM(tb_1.FinalItemSubPartQuantity) FinalItemSubPartQuantity,
SUM(tb_1.FinalItemSubPartQuantity * tb_2.SalesWeek1) TotalSalesWeek1
SUM(tb_1.FinalItemSubPartQuantity * tb_2.SalesWeek2) TotalSalesWeek2
FROM [009Reports].[dbo].[ANC Parts] tb_1
JOIN [555].[cache].[PurchasingSupplyChainNeeds] tb_2 ON tb_1.FinalPartNo = tb_2.ItemNo
GROUP BY tb_1.SubPart
The GROUP BY tb_1.SubPart at the end says you want each unique SubPart on a row, the SUMs in the SELECT explain that you want those values summed for each group.

Can I divide an amount across multiple parties and round to the 'primary' party in a single SQL query?

I am working on an oracle PL/SQL process which divides a single monetary amount across multiple involved parties in a particular group. Assuming 'pGroupRef' is an input parameter, the current implementation first designates a 'primary' involved party, and then it splits the amount across all the secondaries as follows:
INSERT INTO ActualValue
SELECT
...
pGroupRef AS GroupRef,
ROUND(Am.Amount * P.SplitPercentage / 100, 2) AS Amount,
...
FROM
Amount Am,
Party P
WHERE
Am.GroupRef = pGroupRef
AND P.GroupRef = Am.GroupRef
...
P.PrimaryInd = 0;
Finally, it runs a second procedure to insert whatever amount is left over to the primary party, i.e.:
INSERT INTO ActualValue
SELECT
...
pGroupRef AS GroupRef,
Am.Amount - S.SecondaryAmounts,
FROM
Amount Am,
Party P,
(SELECT SUM(Amount) AS SecondaryAmounts FROM ActualValue WHERE GroupRef = pGroupRef) S
WHERE
Am.GroupRef = pGroupRef
AND P.GroupRef = Am.GroupRef
...
P.PrimaryInd = 1;
However, the full query here is very large and I am making this area more complex by adding subgroups, each of which will have their own primary member, and the possibility of overrides - hence if I continued to use this implementation then it would mean a lot of duplicated SQL.
I suppose I could always calculate the correct amounts into an array before running a single unified insert - but I feel like there has to be an elegant mathematical way to capture this logic in a single SQL Query.
So you can use analytical functions to get what you are looking for. As I didn't know your exact structure, this is only an example:
SELECT s.party_id, s.member_id,
s.portion + DECODE(s.prime, 1, s.total - SUM(s.portion) OVER (PARTITION BY s.party_id),0)
FROM (SELECT p.party_id, p.member_id,
ROUND(a.amt*(p.split/100), 2) AS PORTION,
a.amt AS TOTAL, p.prime
FROM party p
INNER JOIN amount a ON p.party_id = a.party_id) s
So in the query you have a subquery that gathers the required information, then the outer query puts everything together, only applying the remainder to the record marked as prime.
Here is a DBFiddle showing how this works (LINK)
N.B.: Interestingly in the example in the DBFiddle, there is a 0.01 overpayment, so the primary actually pays less.

SQL Server - Validation against duplicate values in reverse in two columns [Example Inside]

I am trying to implement validation to check whether the same FX rate value has been entered in reverse order. Users would populate a spreadsheet consisting of three columns:
From Currency
To Currency
Rate
The user would add data into a spreadsheet to import through the system, below is a mock example of how the data would look:
I have the structure of the validation in place, I am just wondering how I would write a select statement in order to return all rows which have both directions (like the first two rows in this example), due to them being the same currency, just in reverse order.
Hmmm, you could use:
select fx.*
from fx
where exists (select 1
from fx fx2
where fx2.fromc = fx.toc and fx2.toc = tx.fromc and fx2.rate = fx.rate
);
Actually, you might extend this check to be sure the values come close to being arithmetic inverses of each other. Because there is some slack with decimal values, I would suggest:
select fx.*
from fx
where exists (select 1
from fx fx2
where fx2.fromc = fx.toc and fx2.toc = tx.fromc and
abs(fx2.rate * fx.rate - 1) > 0.001
);
This checks that the product is off by more than one thousandth. The exact threshold you want depends on your needs.

Oracle Creating Table using Data from Previous Tables with Calculations

I currently have two tables, cts(time, symbol, open, close, high, low, volume) and dividends(time, symbol, dividend). i am attempting to make a third table named, dividend_percent with columns Time, Date and Percent. To get the percentage for the dividend I believe the formula to be ((close-(open+dividend))/open)*100.
The request however exceeded the size allowed by oraclexe and thus failed but i don't believe my request should have been that big.
SQL> create table dividend_percent
2 as (select c.Time, c.Symbol, (((c.close-(c.open+d.dividend))/c.open)*100) P
RCNT
3 from cts c inner join dividend d
4 on c.Symbol=d.Symbol);
from cts c inner join dividend d
*
ERROR at line 3:
ORA-12953: The request exceeds the maximum allowed database size of 11 GB
Am i writing the query wrong or in such a way that's really inefficient? the two tables are big but i don't think too big.
Perhaps you could make a view which combines the two tables and performs the necessary calculations when needed:
CREATE VIEW DIVIDEND_PERCENT_VIEW AS
SELECT c.TIME,
c.SYMBOL,
((c.CLOSE - (c.OPEN + d.DIVIDEND)) / c.OPEN) * 100 AS PRCNT
FROM CTS c
INNER JOIN DIVIDEND d
ON c.SYMBOL = d.SYMBOL AND
c.TIME = d.TIME
WHERE c.OPEN <> 0;
This would avoid duplicating the data, eliminate the need to store everything twice, and perform the PRCNT calculation for data added after the view is created as well as for pre-existing data.
Perhaps you could use materialized view if you are intending to perform DML operations as well as keep the table in sync.