Insert a derived table in SQL into a newly created table - sql

I have two tables(Current and Prior) that have all the same columns and are combined through a full outer join in a query. I also have a derived column for each of the respective columns that compares the values of Current and Prior corresponding fields and says whether they match or not. This creates a derived table that has all the Current and Prior Fields as well as a derived comparing column. I need to create an actual table in a database that captures that data. How would I do that?

This should create a view for you to use, in case the tables are not very large.
CREATE VIEW [dbo].[vw_Compare]
AS
SELECT /* Column list*/
IIF(A.Col1 IS NULL, 1, 0) AS [CompareCol1],
IIF(A.Col2 IS NULL, 1, 0) AS [CompareCol2]
FROM A
FULL OUTER JOIN C
ON A.Col1 = C.Col1
If you wish to create a table:
CREATE TABLE [dbo].[Compare]
(
[CompareCol1] BIT,
[CompareCol2] BIT,
/* Insert Column 1 to N here */
)
INSERT INTO [dbo].[Compare]
(
[CompareCol1],
[CompareCol2],
/* Column list*/
)
SELECT IIF(A.Col1 IS NULL, 1, 0) AS [CompareCol1],
IIF(A.Col2 IS NULL, 1, 0) AS [CompareCol2],
/* Column list*/
FROM A
FULL OUTER JOIN C
ON A.Col1 = C.Col1

Related

SQL Loop and update table with sum

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.

SQL Join With Fallback

Given
CREATE TABLE Addresses
Id INT NOT NULL
Zip NVARCHAR(5) NULL
ZipPlus4 NVARCHAR(9) NULL
CREATE TABLE ZipLookup
Zip NVARCHAR(5) NULL
Code NVARCHAR(10) NULL
CREATE TABLE ZipPlus4Lookup
ZipPlus4 NVARCHAR(9) NULL
Code NVARCHAR(10) NULL
And data like
Addresses
1 | 92123 | 921234444
ZipLookup
92123 | Type A
ZipPlus4Lookup
921234444 | Type B
Is it possible to construct a query such that:
A given row in Addresses is outer joined to ZipPlus4Lookup if there is a match
Addresses.ZipPlus4 = ZipPlus4Lookup.ZipPlus4
Otherwise, the given row in Addresses is outer joined to ZipLookup if there is a match
Addresses.Zip = ZipLookup.Zip
Otherwise neither table is outer joined
In plain English, the Addresses table has a Zip and a ZipPlus4 column and I need to look up a code using the most precise match. If there's a match on Zip+4, use the code from that match. Otherwise, use the code from a Zip match.
I wish I had an attempted query to share, but with this one I don't know where to start.
This basic query will work:
SELECT
A.*,
Code = IsNull(Z4.Code, Z.Code)
FROM
dbo.Addresses A
LEFT JOIN dbo.ZipPlus4Lookup Z4
ON A.ZipPlus4 = Z4.ZipPlus4
LEFT JOIN dbo.ZipLookup Z
ON A.Zip = Z.Zip
AND Z4.ZipPlus4 IS NULL;
Or you could try something like this:
SELECT
A.*,
Z.Code
FROM
dbo.Addresses A
OUTER APPLY (
SELECT TOP 1 Code
FROM (
SELECT 0, Code FROM dbo.ZipPlus4Lookup Z4
WHERE A.ZipPlus4 = Z4.ZipPlus4
UNION ALL
SELECT 1, Code FROM dbo.ZipLookup Z
WHERE A.Zip = Z.Zip
) X (Seq, Code)
ORDER BY X.Seq
) Z;
They may have different performance characteristics. It's worth testing. My guess is the second query is unnecessary but it's still conceptually possible to be better.
See these in action in a SQL Fiddle.

Slow stored procedure

I currently have a stored procedure that copies content from one table to another.
However when it is trying to only insert 27 new rows it continues on for over 12 minutes (after which point I stopped it) it said Affected 27 rows 4 times, however changes were not made.
Can you spot any reason the following SP would be slow?
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER Procedure [dbo].[sp_CopyCompanyContent]
(
#intCopyFromCompanyID Int,
#intNewCompanyID Int
)
As
Begin
/*
RaisError If any of Odl/New Company ID's are 0
*/
If (#intCopyFromCompanyID = 0 Or #intNewCompanyID = 0)
Begin
RaisError('New Company ID or Old Company ID can not be 0', 16, 1)
Return
End
/*
Create Temp Table For the Content Sections
*/
If Object_ID('tempdb..#ContentSections') IS Not Null
Begin
Drop Table dbo.#ContentSections
End
/*
Have to get all the existing data for the Old company we are copying from.
Have to also add the Max(ContentSectionID) From ContentSection. Max(ContentSectionID) +
The Identity (Added further down due to laziness) will be our seed for the ContentID
*/
Select CS.ContentID,
CS.SectionID,
CS.MenuOrder,
#intNewCompanyID NewCompanyID,
CS.CompanyID OldCompanyID,
CS.SubMenu,
CS.Link,
CS.HeaderMenu,
CS.ParentContentID,
CRS.*
Into dbo.#ContentSections
From dbo.Company COMP
Join dbo.ContentSection CS
On COMP.Company_id = CS.CompanyID
Join dbo.Content CONT
On CONT.ContentID = CS.ContentID
Cross Join (
Select MAx(ContentSectionID) MaxContentSectionID
From dbo.ContentSection CONT
) crs
Where COMP.Company_id = #intCopyFromCompanyID
Order By COMP.Company_id
/*
We now need to create a table for the existing content for the old company.
Also have to create the seed. Same principle as above.
*/
If Object_ID('tempdb..#Content') IS Not Null
Begin
Drop Table dbo.#Content
End
Select CONT.*,
CRS.*
Into dbo.#Content
From dbo.Company COMP
Join dbo.ContentSection CS
On COMP.Company_id = CS.CompanyID
Join dbo.Content CONT
On CONT.ContentID = CS.ContentID
Cross Join (
Select MAx(ContentID) MaxContentID
From dbo.Content CONT
) crs
Where COMP.Company_id = #intCopyFromCompanyID
Order By COMP.Company_id
/*
Add Identity to each of the tables we have created above. The ID fields will add to
the Max of each table to mimic what the future seeds will be.
*/
exec('Alter table #ContentSections Add ID Int Identity(1,1)')
exec('Alter table #Content Add ID Int Identity(1,1)')
/*
Add content data from the temp table.
*/
Insert Into dbo.Content
(
Title,
Content
)
Select Title,
Content
From dbo.#Content
/*
Have to the Content table up to the content sections table
as this contains what ID has been add to the Content Table.
*/
Insert Into dbo.ContentSection
(
ContentID,
SectionID,
MenuOrder,
CompanyID,
SubMenu,
Link,
HeaderMenu,
ParentContentID
)
Select C.MaxContentID + C.ID,
CS.SectionID,
CS.MenuOrder,
CS.NewCompanyID,
CS.Submenu,
CS.Link,
CS.HEaderMEnu,
CS.ParentContentID
From dbo.#Content C
Join dbo.#ContentSections CS
On C.ID = CS.ID
End
First thing to do is check the query plan for the selects since cross joins are dangerous beasts, if i'm not reading it wrong you'd display the same value in CRS.* in every record right?
If so, make that query before the select and stored the result in a variable and display it in the select, something like this.
DECLATE #maxValue INTEGER
Select #maxValue=MAX(ContentID) MaxContentID
From dbo.Content CONT
Select CONT.*,
#maxValue as MaxContentID
Into dbo.#Content
From dbo.Company COMP
Join dbo.ContentSection CS
On COMP.Company_id = CS.CompanyID
Join dbo.Content CONT
On CONT.ContentID = CS.ContentID
Where COMP.Company_id = #intCopyFromCompanyID
Order By COMP.Company_id
It is probably because of the identity (it is 2 times from the 4 times*27 rows)
exec('Alter table #ContentSections Add ID Int Identity(1,1)')
exec('Alter table #Content Add ID Int Identity(1,1)')
Instead, if you don't want to create the table, try to use ROW_NUMBER() OVER()... clause as ID, and then you don't need to create the identity later.
And as I see, you don't even need to use the Temp Tables, because then you can simply use the two select as INSERT INTO .... SELECT ... With the ROW_NUMBER().
It seems you don't make any changes on the Temp Tables, so you probably don't need them. (Only if you want to use them out of the SP's scope)

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

querying 2 tables with the same spec for the differences

I recently had to solve this problem and find I've needed this info many times in the past so I thought I would post it. Assuming the following table def, how would you write a query to find all differences between the two?
table def:
CREATE TABLE feed_tbl
(
code varchar(15),
name varchar(40),
status char(1),
update char(1)
CONSTRAINT feed_tbl_PK PRIMARY KEY (code)
CREATE TABLE data_tbl
(
code varchar(15),
name varchar(40),
status char(1),
update char(1)
CONSTRAINT data_tbl_PK PRIMARY KEY (code)
Here is my solution, as a view using three queries joined by unions. The diff_type specified is how the record needs updated: deleted from _data(2), updated in _data(1), or added to _data(0)
CREATE VIEW delta_vw AS (
SELECT feed_tbl.code, feed_tbl.name, feed_tbl.status, feed_tbl.update, 0 as diff_type
FROM feed_tbl LEFT OUTER JOIN
data_tbl ON feed_tbl.code = data_tbl.code
WHERE (data_tbl.code IS NULL)
UNION
SELECT feed_tbl.code, feed_tbl.name, feed_tbl.status, feed_tbl.update, 1 as diff_type
FROM data_tbl RIGHT OUTER JOIN
feed_tbl ON data_tbl.code = feed_tbl.code
where (feed_tbl.name <> data_tbl.name) OR
(data_tbl.status <> feed_tbl.status) OR
(data_tbl.update <> feed_tbl.update)
UNION
SELECT data_tbl.code, data_tbl.name, data_tbl.status, data_tbl.update, 2 as diff_type
FROM feed_tbl LEFT OUTER JOIN
data_tbl ON data_tbl.code = feed_tbl.code
WHERE (feed_tbl.code IS NULL)
)
UNION will remove duplicates, so just UNION the two together, then search for anything with more than one entry. Given "code" as a primary key, you can say:
edit 0: modified to include differences in the PK field itself
edit 1: if you use this in real life, be sure to list the actual column names. Dont use dot-star, since the UNION operation requires result sets to have exactly matching columns. This example would break if you added / removed a column from one of the tables.
select dt.*
from
data_tbl dt
,(
select code
from
(
select * from feed_tbl
union
select * from data_tbl
)
group by code
having count(*) > 1
) diffs --"diffs" will return all differences *except* those in the primary key itself
where diffs.code = dt.code
union --plus the ones that are only in feed, but not in data
select * from feed_tbl ft where not exists(select code from data_tbl dt where dt.code = ft.code)
union --plus the ones that are only in data, but not in feed
select * from data_tbl dt where not exists(select code from feed_tbl ft where ft.code = dt.code)
I would use a minor variation in the second union:
where (ISNULL(feed_tbl.name, 'NONAME') <> ISNULL(data_tbl.name, 'NONAME')) OR
(ISNULL(data_tbl.status, 'NOSTATUS') <> ISNULL(feed_tbl.status, 'NOSTATUS')) OR
(ISNULL(data_tbl.update, '12/31/2039') <> ISNULL(feed_tbl.update, '12/31/2039'))
For reasons I have never understood, NULL does not equal NULL (at least in SQL Server).
You could also use a FULL OUTER JOIN and a CASE ... END statement on the diff_type column along with the aforementioned where clause in querying 2 tables with the same spec for the differences
That would probably achieve the same results, but in one query.