Git Merge in VS2019

  1. Open project in VS 2019.
  2. Go to menu item “Git” at the top and select “Manage Branches”
  3. There will be a list of your local and remote branches.
  4. Select branch “version2” and right mouse and select the item “Merge ‘version2’ into ‘master’ on remote branches

Sync your branch. That’s it.

Insert results of SPROC into temp table

I would like to do something like this;

SELECT * INTO #tmpADD EXEC mySPROC 'Params'

SQL Server doesn’t support this and I don’t want to use OPENQUERY like this.

Select @@ServerName
EXEC sp_serveroption @@ServerName, 'DATA ACCESS', TRUE

SELECT  *
INTO    #tmpADD
FROM    OPENQUERY(YOURSERVERNAME, 'EXEC db.schema.mySPROC 1')

Using OPENROWSET will get the job done, but it will incur some additional overhead for opening up local connections and marshalling data. It also may not be an option in all cases since it requires an ad hoc query permission which poses a security risk and therefore may not be desired. Also, the OPENROWSET approach will preclude the use of stored procedures returning more than one result set. Wrapping multiple inline table-value user-defined functions in a single stored procedure can achieve this.

The first work around is to define structure of return values of stored procedure and create temp table.

CREATE TABLE #tmpADD
(
   COL1 INT,
   COL2 INT
)

INSERT INTO #tmpADD
Exec mySPROC 'Params'

This will work but if we ever add additional columns to the mySPROC stored procedure, this will blow up.

The second work around is to use an inline table-valued user-defined function. This is essentially a stored procedure (will take parameters) that returns a table as a result set; and therefore will place nicely with an INTO statement.

If we still have a driving need for a stored procedure, we can wrap the inline table-valued user-defined function with a stored procedure. The stored procedure just passes parameters when it calls select * from the inline table-valued user-defined function.

So for instance, Here is an inline table-valued user-defined function to get a list of customers for a particular region:

CREATE FUNCTION CustomersByRegion 
(  
    @RegionID int  
)
RETURNS TABLE 
AS
RETURN 
  SELECT *
  FROM customers
  WHERE RegionID = @RegionID
GO

We can then call this function to get the results as such:

SELECT * FROM CustomersbyRegion(1)

Or to do a SELECT INTO:

SELECT * INTO CustList FROM CustomersbyRegion(1)

If we still need a stored procedure, then wrap the function as such:

CREATE PROCEDURE mySPROC
(  
    @regionID int  
)
AS
BEGIN
     SELECT * FROM CustomersbyRegion(@regionID);
END
GO

I think this is the most ‘hack-less’ method to obtain the desired results. It uses the existing features as they were intended to be used without additional complications. By nesting the inline table-valued user-defined function in the stored procedure, we have access to the functionality in two ways. Plus! We have only one point of maintenance for the actual SQL code.

If inline table value functions returns more than one schema then its is not possible to use IF statement in it. In this case we have to use table value function not inline table value function;

-- =============================================
-- Author:		Shahzad Khan
-- Create date: 2/1/2022
-- Description:	returns zoo data
-- SELECT * FROM  [dbo].[fn_getZooAnimal](2)
-- =============================================
CREATE FUNCTION fn_getZooAnimal 
(
	-- Add the parameters for the function here
	@Id int
)
RETURNS 
@Table_Var TABLE 
(
	-- Add the column definitions for the TABLE variable here
	Id int, 
	AnimalName NVARCHAR(50)
)
AS
BEGIN
	-- Fill the table variable with the rows for your result set
	IF (@Id = 1)
	BEGIN
		INSERT @Table_Var
		SELECT @Id, 'Monkey'
	END
	ELSE
	BEGIN
		INSERT @Table_Var
		SELECT @Id, 'Deer'
	END
	RETURN 
END
GO

Resources

https://stackoverflow.com/questions/653714/insert-results-of-a-stored-procedure-into-a-temporary-table?page=1&tab=votes#tab-top

SQL Server Merge Statement

I have 2 tables one containing historical data using type 2 SCD (Slowly changing dimensions) called DimBrand and another containing just the latest dimension data called LatestDimBrand. Using the merge function I will insert new records from LatestDimBrand into DimBrand, I will archive (apply an end date) to any DimBrand records which do not appear in the latest data, and finally enter a new record and archive the old record for any Brands which may have changed.

DimBrand (the target of our Inserts, Updates, Deletes) and DimLatestBrand (the source for Inserts,Updates,Deletes):

DimBrand table
DimLatestBrand table

Here is complete merge statement;

INSERT #DimBrand    ([BrandCode],[BrandName],[StartDate])
SELECT                 [BrandCode],[BrandName],getdate()                                    
FROM                (
                    MERGE #DimBrand AS Target
                    USING    (
                            SELECT    [BrandCode],[BrandName],[StartDate],[EndDate]                       
                            FROM    #LatestDimBrand
                            ) AS Source
                    ON     (Target.[BrandCode] = Source.[BrandCode])    
                    -------------------------------                       
                    WHEN MATCHED AND Target.[BrandName] <> Source.[BrandName]
                        THEN
                        UPDATE SET Target.[EndDate] = getdate()
                    -------------------------------
                    WHEN NOT MATCHED BY TARGET 
                        THEN
                        INSERT ( 
                                [BrandCode]                        
                                ,[BrandName]                    
                                ,[StartDate]              
                                ,[EndDate]                                            
                        )
                        VALUES (      
                                Source.[BrandCode], 
                                Source.[BrandName], 
                                Source.[StartDate],
                                Source.[EndDate]   
                        )
                    -------------------------------
                    WHEN NOT MATCHED BY SOURCE 
                        THEN 
                        UPDATE SET Target.[EndDate] = getdate()
                    -------------------------------
                    OUTPUT $Action, Source.*
                    ) As i([Action],[BrandCode],[BrandName],[StartDate],[EndDate])
                    -------------------------------
WHERE                [Action] = 'UPDATE'
AND                    BrandCode IS NOT NULL

Resources

https://docs.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql?view=sql-server-ver15

https://www.mssqltips.com/sqlservertip/2883/using-the-sql-server-merge-statement-to-process-type-2-slowly-changing-dimensions/

DNS – NSLOOKUP what is the meaning of the non-authoritative answer?

Non-authoritative answer simply means the answer is not fetched from the authoritative DNS server for the queried domain name.

First you have to understand how DNS system works. DNS system can be divided into three tiers. They are:

  • root DNS servers
  • top-level domain DNS servers
  • authoritative DNS servers

There’s another class of DNS Server usually called local DNS server whose IP address is specified on your operating system.

When your browser connects to a website say example.com, the browser first queries your local DNS server to get the IP address of example.com.

  • If the local DNS server doesn’t have the A record of example.com, it will query one of the root DNS servers.
  • The root DNS server will say: I don’t have the A record but I know the top-level domain DNS server which is responsible for .com domains.
  • Then your local DNS server query the top-level domain DNS server which is responsible for .com domains. The TLD DNS server will respond: I don’t know either but I know which DNS server is authoritative for example.com.
  • So your local DNS server queries the authoritative DNS server. Because the actual DNS record is stored on that authoritative DNS server, so it will give your local DNS server an answer.

Then this query result is cached on your local DNS server but it can be outdated. When the TTL time has expired, your local DNS server will update the query result from the authoritative DNS server. Whenever you query a DNS record on your local DNS server, it returns a non-authoritative (unofficial) answer. If you want an authoritative answer, you must explicitly specify the authoritative DNS server when you use nslookup or other utilities. I think a local DNS server should be called caching DNS server.

When someone registers a domain name, he/she can specify which DNS server is the authoritative DNS server. This information is called an NS record. The NS record will tell a top-level domain DNS server which nameserver holds the domain’s A record, MX record, etc.

run nslookup and enter this;

The authoritative name servers for this domain are in red block.

Resource

https://serverfault.com/questions/413124/dns-nslookup-what-is-the-meaning-of-the-non-authoritative-answer

Extract numeric values from text in SQL Server

I have this paragraph;

Speeding ticket is 210.99USD. Aggravated DUI could add up 1 year jail time.

This is a standard text where the pattern is like this;

Speeding ticket is [Amount]. Aggravated DUI could add up [Term] year jail time.

The ask is to extract Amount and Term from this text. The approach is to split the paragraph and use SQL IsNumeric functions to extract the values;

Here is a sample prototype;

DECLARE @ParagraphText NVARCHAR(MAX) = N'Speeding ticket is 210.99USD. Aggrevated DUI could add up 1 year jail time.'

--table variable
DECLARE @Test TABLE (ValueColumn VARCHAR(8000))
INSERT @Test
--I am using a custom function but you can use string_split() on SQL 2017 and upward
SELECT * FROM dbo.fnSplitString(@ParagraphText, ' ')

--using windows ranking function to get both values
SELECT ROW_NUMBER() OVER(ORDER BY ValueColumn) [ROW_NUMBER],*
FROM
(
    SELECT
    CONVERT(DECIMAL(20,8),
    CASE 
	WHEN IsNumeric(ValueColumn)=1 THEN 	CONVERT(FLOAT,ValueColumn)
	ELSE CONVERT(FLOAT,'0'+LEFT(ValueColumn,PATINDEX('%[^0-9.]%',ValueColumn)-1))
    END) AS ExtractedColumn
    ,ValueColumn
    FROM @Test
) x
WHERE x.ExtractedColumn > 0