SQL Loop and update table with sum - sql

I have the following shiftdate table that stores the amount of hours worked at rate1 and rate2 (these are stored in another table). I want to use this data to update the shiftdateTotal field with the total value. Hoping you can help?
SHIFTDATE Table
shiftdateID int
hourlyRate1ID int
hoursRate1 int
hourlyRate2ID int
hoursRate2 int
shiftDateTotal decimal
LISTITEM Table
ListitemID int
description string
rateOfPay decimal
Example listItem row (23, BankHolidayRate, 12.50)
I'm able to create a SQL select that does the required calculation, see below:
SELECT shiftDateID,
hoursRate1, t2.rateOfPay as rate1,
hoursRate2, t3.rateOfPay as rate2,
(hoursRate1 * t2.rateOfPay) + ([hoursRate2] * t3.rateOfPay) as myTotal
from dbo.ShiftDate t1
inner join dbo.ListItem t2
on t1.hourlyRate1ID = t2.listItemID
inner join dbo.ListItem t3
on t1.hourlyRate2ID = t3.listItemID
But now i'm a bit lost.. Using this query or something similar how can I insert the myTotal value into the shiftDateTotal for the required shiftdateID row?
Can i store this into a temp table, then loop through the data and update the SHIFTDATE table with myTotal?

You can just turn this into an update. The transformation is pretty direct:
update sd
set shiftDateTotal = (sd2.hoursRate1 * li1.rateOfPay) + (sd2.hoursRate2 * li2.rateOfPay)
from dbo.ShiftDate sd inner join
dbo.ListItem li1
on sd.hourlyRate1ID = li1.listItemID inner join
dbo.ListItem li2
on sd.hourlyRate2ID = li2.listItemID;
Notes:
I replaced the meaningless table aliases (t1, t2, t3) with more meaningful ones (abbreviations for the table names).
I qualified all column names.
You might want to be careful, in case any of the rate ids are NULL/missing in the original table. In that case, you will want left joins and coalesce()s.

Related

SQL How to get full Array in a temporary table with condition?

Well, I am really sorry because my explanation was so poor. Thank you for all the answers.
I will explain better what should be the output and what is my question.
So, first of I have an array of tagCodes like ('code0','code1','code2').
Then I have a table that contains Codes and TagTypeId.
I would like to get into a temporary table all the codes I passed in the array with their TagTypeId. So a table like:
Code
TagTypeId
903420012408181609019A18
2456
903420012408181609019A18
2135
TestCodeNull
null
So my attempt was this one:
SELECT Tags.Code AS tagCode, Tags.TagTypeId, TagTypes.Code AS tagType
INTO #TempTable
FROM Tags JOIN TagTypes ON Tags.TagTypeId = TagTypes.Id
WHERE Tags.Code IN ('903420012408181609019A18','90341808151313061101E938', 'TestCodeNull')
SELECT * FROM #TempTable;
But I dont get the codes that are not in the Tag table.
I did this an it seems to be working as intended:
CREATE TABLE #TestTable (tagCode NVARCHAR(25) NOT NULL, TagTypeId INT NULL, tagType NVARCHAR(MAX))
INSERT INTO #TestTable (tagCode) VALUES ('903420012408181609019A18'),('00007E08190D0A34E1F524D0'),('00007E08190D0B25E1F5A98B')
UPDATE #TestTable SET TagTypeId = Tags.TagTypeId, tagType = TagTypes.Code FROM #TestTable
LEFT JOIN Tags ON (#TestTable.tagCode = Tags.Code)
LEFT JOIN TagTypes ON (Tags.TagTypeId = TagTypes.Id)
SELECT * FROM #TestTable;
I think what you mean that 'TestCodeNull' does not exist in tags so you want to show null for 'TestCodeNull' in which case a join may be more appropriate. for example
SELECT S.CODE,Tags.Code AS tagCode, Tags.TagTypeId,
TagTypes.Code AS tagType
INTO #TempTable
FROM (select '903420012408181609019A18' code
union all select '90341808151313061101E938'
union all select 'TestCodeNull') s
left join Tags on tags.code = s.code
left JOIN TagTypes ON Tags.TagTypeId = TagTypes.Id
SELECT * FROM #TempTable;

SQL query: Iterate over values in table and use them in subquery

I have a simple SQL table containing some values, for example:
id | value (table 'values')
----------
0 | 4
1 | 7
2 | 9
I want to iterate over these values, and use them in a query like so:
SELECT value[0], x1
FROM (some subquery where value[0] is used)
UNION
SELECT value[1], x2
FROM (some subquery where value[1] is used)
...
etc
In order to get a result set like this:
4 | x1
7 | x2
9 | x3
It has to be in SQL as it will actually represent a database view. Of course the real query is a lot more complicated, but I tried to simplify the question while keeping the essence as much as possible.
I think I have to select from values and join the subquery, but as the value should be used in the subquery I'm lost on how to accomplish this.
Edit: I oversimplified my question; in reality I want to have 2 rows from the subquery and not only one.
Edit 2: As suggested I'm posting the real query. I simplified it a bit to make it clearer, but it's a working query and the problem is there. Note that I have hardcoded the value '2' in this query two times. I want to replace that with values from a different table, in the example table above I would want a result set of the combined results of this query with 4, 7 and 9 as values instead of the currently hardcoded 2.
SELECT x.fantasycoach_id, SUM(round_points)
FROM (
SELECT DISTINCT fc.id AS fantasycoach_id,
ffv.formation_id AS formation_id,
fpc.round_sequence AS round_sequence,
round_points,
fpc.fantasyplayer_id
FROM fantasyworld_FantasyCoach AS fc
LEFT JOIN fantasyworld_fantasyformation AS ff ON ff.id = (
SELECT MAX(fantasyworld_fantasyformationvalidity.formation_id)
FROM fantasyworld_fantasyformationvalidity
LEFT JOIN realworld_round AS _rr ON _rr.id = round_id
LEFT JOIN fantasyworld_fantasyformation AS _ff ON _ff.id = formation_id
WHERE is_valid = TRUE
AND _ff.coach_id = fc.id
AND _rr.sequence <= 2 /* HARDCODED USE OF VALUE */
)
LEFT JOIN fantasyworld_FantasyFormationPlayer AS ffp
ON ffp.formation_id = ff.id
LEFT JOIN dbcache_fantasyplayercache AS fpc
ON ffp.player_id = fpc.fantasyplayer_id
AND fpc.round_sequence = 2 /* HARDCODED USE OF VALUE */
LEFT JOIN fantasyworld_fantasyformationvalidity AS ffv
ON ffv.formation_id = ff.id
) x
GROUP BY fantasycoach_id
Edit 3: I'm using PostgreSQL.
SQL works with tables as a whole, which basically involves set operations. There is no explicit iteration, and generally no need for any. In particular, the most straightforward implementation of what you described would be this:
SELECT value, (some subquery where value is used) AS x
FROM values
Do note, however, that a correlated subquery such as that is very hard on query performance. Depending on the details of what you're trying to do, it may well be possible to structure it around a simple join, an uncorrelated subquery, or a similar, better-performing alternative.
Update:
In view of the update to the question indicating that the subquery is expected to yield multiple rows for each value in table values, contrary to the example results, it seems a better approach would be to just rewrite the subquery as the main query. If it does not already do so (and maybe even if it does) then it would join table values as another base table.
Update 2:
Given the real query now presented, this is how the values from table values could be incorporated into it:
SELECT x.fantasycoach_id, SUM(round_points) FROM
(
SELECT DISTINCT
fc.id AS fantasycoach_id,
ffv.formation_id AS formation_id,
fpc.round_sequence AS round_sequence,
round_points,
fpc.fantasyplayer_id
FROM fantasyworld_FantasyCoach AS fc
-- one row for each combination of coach and value:
CROSS JOIN values
LEFT JOIN fantasyworld_fantasyformation AS ff
ON ff.id = (
SELECT MAX(fantasyworld_fantasyformationvalidity.formation_id)
FROM fantasyworld_fantasyformationvalidity
LEFT JOIN realworld_round AS _rr
ON _rr.id = round_id
LEFT JOIN fantasyworld_fantasyformation AS _ff
ON _ff.id = formation_id
WHERE is_valid = TRUE
AND _ff.coach_id = fc.id
-- use the value obtained from values:
AND _rr.sequence <= values.value
)
LEFT JOIN fantasyworld_FantasyFormationPlayer AS ffp
ON ffp.formation_id = ff.id
LEFT JOIN dbcache_fantasyplayercache AS fpc
ON ffp.player_id = fpc.fantasyplayer_id
-- use the value obtained from values again:
AND fpc.round_sequence = values.value
LEFT JOIN fantasyworld_fantasyformationvalidity AS ffv
ON ffv.formation_id = ff.id
) x
GROUP BY fantasycoach_id
Note in particular the CROSS JOIN which forms the cross product of two tables; this is the same thing as an INNER JOIN without any join predicate, and it can be written that way if desired.
The overall query could be at least a bit simplified, but I do not do so because it is a working example rather than an actual production query, so it is unclear what other changes would translate to the actual application.
In the example I create two tables. See how outer table have an alias you use in the inner select?
SQL Fiddle Demo
SELECT T.[value], (SELECT [property] FROM Table2 P WHERE P.[value] = T.[value])
FROM Table1 T
This is a better way for performance
SELECT T.[value], P.[property]
FROM Table1 T
INNER JOIN Table2 p
on P.[value] = T.[value];
Table 2 can be a QUERY instead of a real table
Third Option
Using a cte to calculate your values and then join back to the main table. This way you have the subquery logic separated from your final query.
WITH cte AS (
SELECT
T.[value],
T.[value] * T.[value] as property
FROM Table1 T
)
SELECT T.[value], C.[property]
FROM Table1 T
INNER JOIN cte C
on T.[value] = C.[value];
It might be helpful to extract the computation to a function that is called in the SELECT clause and is executed for each row of the result set
Here's the documentation for CREATE FUNCTION for SQL Server. It's probably similar to whatever database system you're using, and if not you can easily Google for it.
Here's an example of creating a function and using it in a query:
CREATE FUNCTION DoComputation(#parameter1 int)
RETURNS int
AS
BEGIN
-- Do some calculations here and return the function result.
-- This example returns the value of #parameter1 squared.
-- You can add additional parameters to the function definition if needed
DECLARE #Result int
SET #Result = #parameter1 * #parameter1
RETURN #Result
END
Here is an example of using the example function above in a query.
SELECT v.value, DoComputation(v.value) as ComputedValue
FROM [Values] v
ORDER BY value

SQL Server 2014 Replace Distinct? DeIdentify Data

I am going to explain again what I am trying to do in hopes that you can help.
Table 1 has 4061 rows with columns that include
[Name],[Address1],[Address2],[Address3],[City],[State],[Zip],[Country],[Phone]
and 20 other columns. Table 1 is data that needs to be deidentified. Table 1 has 1534 distinct [Name] rows out of 4061 rows total.
Table 2 has auto generated data which includes the same columns. I would like to replace the above mentioned columns in table 1 with data from table 2. I want to select distinct based on [Name] from table one and then [Name],[Address1],[Address2],[Address3],[City],[State],[Zip],[Country],[Phone] with a new set of distinct data from table 2.
I do not want to just update each row with a new address as that will screw up the data consistency. By replacing only distinct this will allow me to preserve the data consistency while changing the row data in table 1. When I am done I would like to have 1534 distinct new de-identified [Name] [Address1],[Address2],[Address3],[City],[State],[Zip],[Country],[Phone] in table 1 from table 2.
You would use join in the update. You can generate a join key for 1500 rows using row_number():
update toupdate
set t.address = f.address
from (select t.*, row_number() over (order by newid()) as seqnum
from table t
) toupdate join
(select f.*, row_number() over (order by newid()) as seqnum
fake f
) f
on toupdate.seqnum = f.seqnum and t.seqnum <= 1500;
Here is how I ended up doing it.
First I ran a statement to select distinct and inserted it into a table.
Select Distinct [Name],[Address1],[City],[State],[Zip],[Country],[Phone]
INTO APMAST2
FROM APMAST
I then added name2 column in APMAST2 and used a statement to create a sequential id field into APMAST2.
DECLARE #id INT
SET #id = 0
UPDATE APMAST2
SET #id = id = #id + 1
GO
Now I have my distinct info plus a blank name field and a sequential ID field in APMAST2. Now I can join this date with my fakenames table which I generated from. HERE using their bulk tool.
Using a Join Statement I joined my fake data with APMAST2
Update dbo.APMAST2
SET dbo.APMAST2.Name = dbo.fakenames.company,
dbo.APMAST2.Address1 = dbo.fakenames.streetaddress,
dbo.APMAST2.City = dbo.fakenames.City,
dbo.APMAST2.State = dbo.fakenames.State,
dbo.APMAST2.Zip = dbo.fakenames.zipcode,
dbo.APMAST2.Country = dbo.fakenames.countryfull,
dbo.APMAST2.Phone = dbo.fakenames.telephonenumber
FROM
dbo.APMAST2
INNER JOIN
dbo.fakenames
ON dbo.fakenames.number = dbo.APMAST2.id
Now I have my fake data loaded but I kept my original Name field so I could reload this data into my full table ARMAST so now I can do a join between ARMAST2 and ARMAST.
Update dbo.APMAST
SET dbo.APMAST.Name = dbo.APMAST2.Name,
dbo.APMAST.Address1 = dbo.APMAST2.Address1,
dbo.APMAST.City = dbo.APMAST2.City,
dbo.APMAST.State = dbo.APMAST2.State,
dbo.APMAST.Zip = dbo.APMAST2.Zip,
dbo.APMAST.Country = dbo.APMAST2.Country,
dbo.APMAST.Phone = dbo.APMAST2.Phone
FROM
dbo.APMAST
INNER JOIN
dbo.apmast2
ON dbo.apmast.name = dbo.APMAST2.name2
Now my original table has all fake data in it but it keeps the integrity it had , well most of it, so the data looks good when reported on but is de-identified. You can now remove APMAST2 or keep it if you need to match this with other data later on. I know this is long and I am sure there is a better way to do it but this is how I did it, suggestions welcome.

Using a LEFT OUTER JOIN WHERE then updating same data

I have two databases running on MSSQL 2005, SOURCE and DESTINATION, which have the same structure and tables in them.
I'm trying to update data from s to d.
In this example, I'm trying to copy data from s to d using a join and only bringing across entries which aren't already in d.
I'm then trying to update the same records just inserted with vales thus:
INSERT DESTINATION.ITEM_REPLENISH_VENDOR ([ITEM_CODE],[VEND_CODE],[PRIMARY_VENDOR],[PURCHASE_MEASURE],[STD_COST],[LAST_COST],[EOQ],[VENDOR_PART_NO],[LEAD_TIME],[COST])
SELECT s.[ITEM_CODE],s.[VEND_CODE],s.[PRIMARY_VENDOR],s.[PURCHASE_MEASURE],s.[STD_COST],s.[LAST_COST],s.[EOQ],s.[VENDOR_PART_NO],s.[LEAD_TIME], s.[COST] FROM SOURCE.dbo.ITEM_REPLENISH_VENDOR s
LEFT OUTER JOIN DESTINATION.dbo.ITEM_REPLENISH_VENDOR d ON (d.ITEM_CODE = s.ITEM_CODE)
WHERE d.ITEM_CODE IS NULL
UPDATE DESTINATION.dbo.ITEM_REPLENISH_VENDOR
SET VEND_CODE='100004', PRIMARY_VENDOR='T',STD_COST='0',LAST_COST='0',COST='0'
WHERE
My issue is once I reach the second WHERE I don't know how to refer to the data I've just updated. This script is going to run either every day at a set time and I don't want to overwrite that whole column with those values, just the entries that have been inserted on this execution.
It looks like you want the output clause This will let you stash away the inserted values.
-- item_code needs to have the same type as the source table
declare #inserted table (item_code int not null primary key);
insert destination.item_replenish_vendor (
[item_code], [vend_code], [primary_vendor],
[purchase_measure], [std_cost], [last_cost],
[eoq], [vendor_part_no], [lead_time],[cost]
) -- save inserted values
output
inserted.item_code into #inserted
select
s.[item_code], s.[vend_code], s.[primary_vendor],
s.[purchase_measure], s.[std_cost], s.[last_cost],
s.[eoq], s.[vendor_part_no], s.[lead_time], s.[cost]
from
source.dbo.item_replenish_vendor s
left outer join
destination.dbo.item_replenish_vendor d
on d.item_code = s.item_code
where
d.item_code is null;
update
d
set
vend_code = '100004',
primary_vendor = 'T',
std_cost = '0',
last_cost = '0,
cost = '0'
from
destination.dbo.item_replenish_vendor d
inner join
#inserted i
on d.item_code = i.item_code;
In this case, you could just put constant values in the insert statement, instead of doing things in two steps...
In the example you have:
UPDATE DESTINATION.dbo.ITEM_REPLENISH_VENDOR
SET VEND_CODE='100004', PRIMARY_VENDOR='T',STD_COST='0',LAST_COST='0',COST='0'
If your VEND_CODE, PRIMARY_VENDOR, STD_COST, LAST_COST, COST are always going to be a static value, you could just put them into the first query.
INSERT DESTINATION.ITEM_REPLENISH_VENDOR ([ITEM_CODE],[VEND_CODE],[PRIMARY_VENDOR],[PURCHASE_MEASURE],[STD_COST],[LAST_COST],[EOQ],[VENDOR_PART_NO],[LEAD_TIME],[COST])
SELECT s.[ITEM_CODE],'100004','T',s.[PURCHASE_MEASURE],'0','0',s.[EOQ],s.[VENDOR_PART_NO],s.[LEAD_TIME], '0'
FROM SOURCE.dbo.ITEM_REPLENISH_VENDOR s
LEFT OUTER JOIN DESTINATION.dbo.ITEM_REPLENISH_VENDOR d ON (d.ITEM_CODE = s.ITEM_CODE)
WHERE d.ITEM_CODE IS NULL
but if they do need to be calculated after insert, then I agree with Laurence's approach.

How can I select a subset of columns from a table when relevant in an outer join?

select a.cust_xref_id, a.est_hour, a.phone_nbr as number, a.credit_calls, a.credit_rpcs, b.sdp_calls
from #temp0 a
full outer join #temp2 b
on a.cust_xref_id = b.sdp_cust_xref_id
and a.est_hour = b.sdp_hour
and a.phone_nbr = b.sdp_phone
Is there a way to get the data from table b with regard to sdp_cust_xref_id, sdp_hour, and sdp_phone when the data does not exist in both tables via the join? If b.sdp_calls does exist, the column values are null.
I read it a few more times and I think I know what you want. Try this. It will give you the values from table b if they are NULL in a:
select COALESCE(a.cust_xref_id, b.sdp_cust_xref_id) as cust_xref_id,
COALESCE(a.est_hour, b.spd_hour) as est_hour,
COALESCE(a.phone_nbr, b.spd_phone) as number,
a.credit_calls,
a.credit_rpcs,
b.sdp_calls
from #temp0 a
full outer join #temp2 b
on a.cust_xref_id = b.sdp_cust_xref_id
and a.est_hour = b.sdp_hour
and a.phone_nbr = b.sdp_phone