Displaying data from different tables and displaying them In details with summary - sql

I am very beginner and I am just learning by myself, so please excuse if I can't even express what I want to say in Programmer way!
I am trying to develop simple business application that can do this:
Register Employee or Customer.
Can store expenses or money that employee can take in advance before salary payment (This can be done several times, or it may be
zero)
Can store payments (Salary given to the employee) - It can be also done several times
It can calculate all expenses, calculate all payments, then find their difference, hence will be paid the remaining of the balance!
CREATE TABLE Customer
( CustomerID INT IDENTITY (1000,1)
,FirstName VARCHAR(40)
,RegDate DATETIME DEFAULT GETDATE()
,Phone VARCHAR(10)
);
CREATE TABLE Expense
( ExpenseID INT IDENTITY (1000,1)
,CustomerID INT NOT NULL
,ExpDate DATETIME DEFAULT GETDATE()
,Amount MONEY
,Comment VARCHAR (100)
);
CREATE TABLE TblLoad
( LoadID INT IDENTITY (1000,1) --PRIMARY KEY
,CustomerID INT NOT NULL
,DepDate DATETIME DEFAULT GETDATE()
,Amount MONEY
,FromTo VARCHAR (100)
);
select Customer.CustomerID, Customer.FirstName, Expense.Amount As ExpenseAmount from Customer inner join
Expense on Customer.CustomerID = Expense.CustomerID
WHERE Customer.CustomerID = 1000
select Customer.CustomerID, Customer.FirstName, TblLoad.Amount As LoadAmount from Customer inner join
TblLoad on Customer.CustomerID = TblLoad.CustomerID
WHERE Customer.CustomerID = 1000
SELECT Customer.CustomerID, Customer.FirstName, e.SE as AmountExp , l.SL as AmountLoad , (l.SL - e.SE) as FinalPay
FROM Customer
INNER JOIN (SELECT CustomerID, SUM(Amount) AS SE FROM Expense GROUP BY CustomerID) E
ON Customer.CustomerID = e.CustomerID
INNER JOIN (SELECT CustomerID, SUM(Amount) AS SL FROM TblLoad GROUP BY CustomerID) L
ON Customer.CustomerID = l.CustomerID
WHERE Customer.CustomerID = 1000
Attached are pictures of what I expected, and what I was willing
Expected Result
Result From The Code

Firstly congrats on getting as far as you have. The bad news is that t-sql is not a reporting tool - you need to look elsewhere if you want pretty output (SSRS,Crystal reports, excel etc,etc) to get anywhere near to your required ouput in t-sql you need to get a bit creative for example:-
/*
drop table customer
drop table expense
drop table TblLoad
CREATE TABLE Customer
( CustomerID INT --IDENTITY (1000,1)
,FirstName VARCHAR(40)
,RegDate DATETIME --DEFAULT GETDATE()
,Phone VARCHAR(10)
);
INSERT INTO CUSTOMER
( CustomerID
,FirstName
,RegDate
,Phone )
VALUES
(1001,'Emp1',cast('2015/10/20 00:00:00.000' as datetime), null),
(1002,'Emp2',cast('2015/10/22 00:00:00.000' as datetime), null),
(1003,'Emp3',cast('2015/10/25 00:00:00.000' as datetime), null)
CREATE TABLE Expense
( ExpenseID INT --IDENTITY (1000,1)
,CustomerID INT NOT NULL
,ExpDate DATETIME DEFAULT GETDATE()
,Amount MONEY
,Comment VARCHAR (100)
);
truncate table expense
insert into expense
( ExpenseID
,CustomerID
,ExpDate
,Amount
--,Comment
)
Values
(1001,1001,cast('2015/10/21' as datetime),100),
(1002,1001,cast('2015/11/22' as datetime),20),
(1003,1001,cast('2015/12/25' as datetime),80),
(1004,1002,cast('2015/11/21' as datetime),100),
(1005,1002,cast('2015/12/25' as datetime),200),
(1006,1003,cast('2015/11/25' as datetime),300)
CREATE TABLE TblLoad
( LoadID INT --IDENTITY (1000,1) --PRIMARY KEY
,CustomerID INT NOT NULL
,DepDate DATETIME DEFAULT GETDATE()
,Amount MONEY
,FromTo VARCHAR (100)
);
truncate table tblload
insert into tblload
( loadID
,CustomerID
,depDate
,Amount
--,Comment
)
Values
(1001,1001,cast('2015/10/21' as datetime),1000),
(1002,1001,cast('2015/11/21' as datetime),200),
(1003,1001,cast('2015/12/24' as datetime),800),
(1004,1002,cast('2015/11/21' as datetime),2000),
(1005,1002,cast('2015/12/25' as datetime),500),
(1006,1003,cast('2015/11/24' as datetime),4000)
*/
select
--case
--when right(rtrim(cast(yyyymm as char(8))) ,2 ) = '13' then char(30)
--else cast(yyyymm as char(8))
--end
case
when right(rtrim(cast(yyyymm as char(8))) ,2 ) = '13' then char(30)
else cast(customerid as char(4))
end
,case
when right(rtrim(cast(yyyymm as char(8))) ,2 ) = '13' then 'Total'
else firstname
end
,sum(amountexp) amuntexp
,sum(amountload) amountload
,sum(amountload) - sum(amountexp) finalpay
from
(
select year(e.expdate) * 100 + month(e.expdate) yyyymm,
C.CustomerID, c.FirstName, sum(e.amount) as AmountExp , 0 as AmountLoad , 0 as FinalPay
FROM Customer c
INNER JOIN Expense E ON C.CustomerID = e.CustomerID
group by year(e.expdate) , month(e.expdate),
C.CustomerID, c.FirstName
union
select year(e.expdate) * 100 + 13 ,
C.CustomerID, c.FirstName, sum(e.amount) as AmountExp , 0 as AmountLoad , 0 as FinalPay
FROM Customer c
INNER JOIN Expense E ON C.CustomerID = e.CustomerID
group by year(e.expdate) * 100 + 13,
C.CustomerID, c.FirstName
union
select year(l.depdate) * 100 + month(l.depdate) yyyymm,
C.CustomerID, c.FirstName, 0 asAmountExp , sum(l.amount) as AmountLoad , 0 as FinalPay
FROM Customer c
INNER JOIN tblload l ON C.CustomerID = l.CustomerID
group by year(l.depdate) , month(l.depdate),
C.CustomerID, c.FirstName
union
select year(l.depdate) * 100 + 13 yyyymm,
C.CustomerID, c.FirstName, 0 asAmountExp , sum(l.amount) as AmountLoad , 0 as FinalPay
FROM Customer c
INNER JOIN tblload l ON C.CustomerID = l.CustomerID
group by year(l.depdate) * 100 + 13,
C.CustomerID, c.FirstName
) s
group by s.CustomerID,s.firstname,yyyymm
order by s.CustomerID,s.firstname,yyyymm

Related

Can we reduce multiple joins to same table

I have following stored procedure:
ALTER PROCEDURE GetNotes
#ResidentId INT,
#Type INT = 0
AS
BEGIN
SET NOCOUNT ON;
DECLARE #myTab TABLE
(
Residentid int NOT NULL,
Description nvarchar(max) NULL,
CreatedOn DATETIME2(7) NULL,
CreatedBy NVARCHAR(MAX) NULL,
NoteTypeId int NULL
)
INSERT INTO #myTab (Residentid, Description, CreatedOn, CreatedBy, NoteTypeId)
SELECT
R.Id,
v.[Description],
v.[Date],
v.[CreatedBy],
v.Col
FROM
Resident R
LEFT JOIN
Invoice AS I ON I.ResidentId = R.Id
LEFT JOIN
ResidentCourse AS RC ON RC.ResidentId = R.Id
--- LEFT JOIN ON USERS FOR Getting CreatedBy Column ---
LEFT JOIN
USERS AS Ui ON I.CreatedBy = Ui.Id
LEFT JOIN
USERS AS Urc ON rc.CreatedBy = Urc.Id
LEFT JOIN
Resident AS UR ON R.CreatedBy = UR.Id
CROSS APPLY
(VALUES (1, R.CreatedDate, R.Notes, UR.FirstName + ' '+ UR.LastName),
(3, I.Date, I.Description, UI.FirstName + ' '+ UI.LastName),
(4, RC.Date, RC.Notes, URC.FirstName + ' '+ URC.LastName),
) v (Col, [Date], [Description], [CreatedBy])
WHERE
R.Id = #ResidentId
SELECT DISTINCT
Residentid,
Description,
CreatedBy,
CreatedOn,
NoteTypeId
FROM
#myTab
WHERE
Description IS NOT NULL
AND CreatedOn IS NOT NULL
AND (#Type = 0 OR NoteTypeId = #Type)
ORDER BY
CreatedOn DESC
END
Result
Id Description CreatedBy CreatedOn NoteTypeId
-------- ---------- ------------------------------------------
1 ASD ASD John Doe 2020-01-02 1
4 ASD DSS Terry kal 2020-01-02 3
I have a Note column in every table and a CreatedBy column also so in above stored procedure I am performing a LEFT JOIN on the Users table for getting the name of that user. Considering I have 8 different tables with Note / Description columns, right now I have 8 LEFT JOIN against the Users table to get data.
Is there an optimal solution?
Any help would be appreciated.
Instead of joining I, R and RC to Users, you could put each CreatedBy in #MyTab and join in the second query.
DECLARE #myTab TABLE
(
Residentid int NOT NULL,
Description nvarchar(max) NULL,
CreatedOn DATETIME2(7) NULL,
CreatedBy int NULL,
NoteTypeId int NULL
)
INSERT INTO #myTab (Residentid, Description, CreatedOn, CreatedBy, NoteTypeId)
SELECT
R.Id,
v.[Description],
v.[Date],
v.[CreatedBy],
v.Col
FROM
Resident R
LEFT JOIN
Invoice AS I ON I.ResidentId = R.Id
LEFT JOIN
ResidentCourse AS RC ON RC.ResidentId = R.Id
CROSS APPLY
(VALUES (1, R.CreatedDate, R.Notes, UR.CreatedBy ),
(3, I.Date, I.Description, UI.CreatedBy ),
(4, RC.Date, RC.Notes, URC.CreatedBy ),
) v (Col, [Date], [Description], [CreatedBy])
WHERE
R.Id = #ResidentId
SELECT DISTINCT
T.Residentid,
T.Description,
U.FirstName + ' ' + U.LastName,
T.CreatedOn,
T.NoteTypeId
FROM
#myTab AS T
LEFT JOIN
USERS AS U ON T.CreatedBy = U.Id
WHERE
Description IS NOT NULL
AND CreatedOn IS NOT NULL
AND (#Type = 0 OR NoteTypeId = #Type)
ORDER BY
CreatedOn DESC
Give this a whirl:
WITH some_cte AS (
SELECT 'Resident' AS DataSource
, Id AS ResidentId
, Notes AS Description
, CreatedDate AS CreatedOn
, CreatedBy AS CreatedByUserId
FROM Resident
WHERE Notes IS NOT NULL
AND CreatedDate IS NOT NULL
AND Id = #ResidentId
AND #Type IN (0, 1)
UNION ALL
SELECT 'Invoice' AS DataSource
, ResidentId
, Description
, [Date] AS CreatedOn
, CreatedBy AS CreatedByUserId
FROM Description IS NOT NULL
AND [Date] IS NOT NULL
AND ResidentId = #ResidentId
AND #Type IN (0, 3)
UNION ALL
SELECT 'ResidentCourse' AS DataSource
, ResidentId
, Notes AS Description
, [Date] AS CreatedOn
, CreatedBy AS CreatedByUserId
FROM ResidentCourse
WHERE Notes IS NOT NULL
AND [Date] IS NOT NULL
AND ResidentId = #ResidentId
AND #Type IN (0, 4)
)
SELECT some_cte.DataSource
, some_cte.ResidentId
, some_cte.CreatedOn
, some_cte.Description
, some_cte.CreatedByUserId
, Users.FirstName + ' ' + Users.LastName AS CreatedBy
FROM some_cte
INNER
JOIN Users
ON Users.Id = some_cte.CreatedByUserId
ORDER
BY some_cte.CreatedOn DESC
;
The join to Users is now only performed once, but might be sub-optimal. Should it be a problem you can inline the join to Users on each individual query.

Return columns from different tables that are after a given date

I need to write a query that returns product orders that were open during April of 2018 and are still open and also returns product orders that were open during April of 2018 and are no longer open.
The rows need to include in the results of the name of the customer that placed the order, the id for the order, and the date the order was filled.
Here is the table info
CREATE TABLE dbo.ProductOrders
(
POID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY ,
ProductId INT NOT NULL
CONSTRAINT FK_ProductOrders_ProductId_ref_Products_ProductId
FOREIGN KEY REFERENCES dbo.Products ( ProductId ) ,
CustomerId INT NOT NULL ,
OrderedQuantity INT ,
Filled BIT NOT NULL
CONSTRAINT DF_ProductOrders_Filled
DEFAULT ( 0 ) ,
DateOrdered DATETIME
CONSTRAINT DF_ProductOrders_DateOrdered
DEFAULT ( GETDATE()) ,
DateFilled DATETIME
CONSTRAINT DF_ProductOrders_DateFilled
DEFAULT ( GETDATE())
);
INSERT dbo.ProductOrders ( ProductId ,
CustomerId ,
OrderedQuantity ,
Filled ,
DateOrdered ,
DateFilled )
VALUES ( 2, 1, 1000, 0, '4/16/18 8:09:13', NULL ) ,
( 2, 1, 500, 1, '3/27/18 17:00:21', '6/24/18 13:29:01' ) ,
( 3, 3, 2000, 1, '12/01/04 13:28:58', '2/19/05 19:41:42' ) ,
( 1, 1, 632, 0, '5/23/18 4:25:52', NULL ) ,
( 4, 4, 901, 0, '3/30/18 21:30:28', NULL );
CREATE TABLE dbo.Customers
(
CustomerId INT NOT NULL IDENTITY(1, 1) PRIMARY KEY ,
CustomerName NVARCHAR(100) ,
Active BIT NOT NULL
CONSTRAINT DF_Customers_Active
DEFAULT ( 1 )
);
INSERT dbo.Customers ( CustomerName ,
Active )
VALUES ( 'Bikes R'' Us', 1 ) ,
( 'Industrial Giant', 1 ) ,
( 'Widget-Works', 0 ) ,
( 'Custom Hangers', 1 );
This is my best attempt at it, I know this is not the right syntax but I'm not sure if I need a join between these to tables to make this work or how I would go about selecting orders that start at April 2018 and also are open or closed after that date.
select CustomerName, POID, DataFilled,
From ProductOrders, Customers
Where DateOrdered is >= April 2018
I think you want and join and filtering:
select c.customername, po.poid, po.dateordered, po.datefilled
from productorders po
inner join customers c on c.customerid = po.customerid
where
po.dateordered >= '20180401'
and po.dateordered < '20180501'
and po.datefilled < getdate()
This gives you orders that were ordered in April 2018 and are not open anymore as of now. To get orders that are still open, you would change the last condition to po.datefilled is null.

Designing a slowly changing dimension type 2 script with postgresql

Let's say I have the following target table:
CREATE TABLE DimCustomer (
CustomerKey serial PRIMARY KEY,
CustomerNum int NOT NULL,
CustomerName varchar(25) NOT NULL,
Planet varchar(25) NOT NULL,
RowIsCurrent char(1) NOT NULL DEFAULT 'Y',
RowStartDate date NOT NULL DEFAULT CURRENT_TIMESTAMP,
RowEndDate date NOT NULL DEFAULT '12/31/9999'
);
INSERT INTO DimCustomer
(CustomerNum, CustomerName, Planet, RowStartDate)
VALUES (101,'Anakin Skywalker', 'Tatooine', CURRENT_TIMESTAMP - INTERVAL '101 days'),
(102,'Yoda', 'Coruscant', CURRENT_TIMESTAMP - INTERVAL '100 days'),
(103,'Obi-Wan Kenobi', 'Coruscant', CURRENT_TIMESTAMP - INTERVAL '100 days')
And I have a following staging table:
CREATE TABLE Staging_DimCustomer
(
CustomerNum int NOT NULL,
CustomerName varchar(25) NOT NULL,
Planet varchar(25) NOT NULL,
ChangeDate date NOT NULL DEFAULT CURRENT_TIMESTAMP,
RankNo int NOT NULL DEFAULT 1
)
INSERT INTO Staging_DimCustomer(CustomerNum, CustomerName, Planet, ChangeDate)
VALUES
(103,'Ben Kenobi', 'Coruscant', CURRENT_TIMESTAMP - INTERVAL '99 days')
In the staging table, it looks like 'Obi-Wan Kenobi'(customernum 103) changed his name to
'Ben Kenobi'. I want to create a script that implements scd type 2 and produces the following result(slowly changing dimension type 2):
Following is my attempt:
INSERT INTO DimCustomer (
CustomerNum, CustomerName, Planet, RowIsCurrent, RowStartDate, RowEndDate
)
select CustomerNum, CustomerName, Planet, 'Y', ChangeDate, '12/31/9999'
from Staging_DimCustomer
ON CONFLICT (CustomerNum) and RowIsCurrent = 'Y'
DO UPDATE SET
CustomerName = EXCLUDED.CustomerName,
Planet = EXCLUDED.Planet,
RowIsCurrent = 'N',
RowEndDate = EXCLUDED.ChangeDate
I dont know how to look for the values that changed, update the existing rows to retire it and then insert the new rows with rowiscurrent = 'Y' flag. I am trying to model my solution based on this sql server article
http://www.made2mentor.com/2013/08/how-to-load-slowly-changing-dimensions-using-t-sql-merge/.
Assuming the changes are all on the most current row, then you can update the current row and then insert:
with u as (
update dimCustomer c
set RowIsCurrent = 'N',
RowEndDate = sc.ChangeDate
from Staging_DimCustomer sc
where sc.CustomerNum = c.CustomerNum and
c.RowIsCurrent = 'Y'
)
insert into dimCustomer (CustomerNum, CustomerName, Planet, RowIsCurrent, RowStartDate, RowEndDate
)
select CustomerNum, CustomerName, Planet, 'Y', ChangeDate, '9999-12-31'::date
from Staging_DimCustomer sc;
This assumes that the changes take place on the most current record. It is rather trickier to implement historic changes, and I'm guessing that is not necessary.
Note that you might want an additional check that the row being inserted is actually different from the current row.
EDIT:
If you want to avoid changes for rows that already exist, you can do:
with sc as (
select *
from Staging_DimCustomer
where not exists (select 1
from DimCustomer c
where c.CustomerNum = sc.CustomerNum and
c.CustomerName = sc.CustomerName and
. . . -- whatever other columns you want to check
)
),
u as (
update dimCustomer c
set RowIsCurrent = 'N',
RowEndDate = sc.ChangeDate
from sc
where sc.CustomerNum = c.CustomerNum and
c.RowIsCurrent = 'Y'
)
insert into dimCustomer (CustomerNum, CustomerName, Planet, RowIsCurrent, RowStartDate, RowEndDate
)
select CustomerNum, CustomerName, Planet, 'Y', ChangeDate, '9999-12-31'::date
from sc;
I think this should work fine , not updating or inserting the already existing records:
with us as (
update dimCustomer c
set RowIsCurrent = 'N',
RowEndDate = sc.ChangeDate
from Staging_DimCustomer sc
where sc.CustomerNum = c.CustomerNum and
c.RowIsCurrent = 'Y' and
sc.customername <> c.customername
),
u as (
select stg.customernum,stg.customername,stg.planet ,stg.changedate from Staging_DimCustomer stg
Inner join DimCustomer dim on dim.customernum=stg.customernum and dim.rowiscurrent='Y'
and (dim.customername <> stg.customername
or dim.planet <> stg.planet
)
UNION
select stg.customernum,stg.customername,stg.planet ,stg.changedate from Staging_DimCustomer stg
where stg.customernum not IN(select dim.customernum from DimCustomer dim where dim.rowiscurrent='Y')
)
insert into dimCustomer (CustomerNum, CustomerName, Planet, RowIsCurrent, RowStartDate, RowEndDate
)
select CustomerNum, CustomerName, Planet, 'Y', ChangeDate, '9999-12-31'::date
from u ;

Why does my insert for new records only does not work even if I'm using where not exits to a subquery?

This is the script I used to create the table.
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[ODSCustomerBase]') AND type in (N'U'))
DROP TABLE [ODSCustomerBase]
Go
Create Table ODSCustomerBase
(CustomerBaseID int NOT NULL identity primary key,
RecordSource nvarchar(4),
RecordType varchar(2),
SiteURN nvarchar(128)NOT NULL,
SiteDesc nvarchar(60)NULL,
CustomerLink nvarchar(120)NOT NULL,
HomeCurrencyCode nvarchar(8)NOT NULL,
CustomerID nvarchar(15)NOT NULL,
CustomerCurrencyCode nvarchar(8)NOT NULL,
CustomerName nvarchar(120)NULL,
CustomerShortName nvarchar(30) NOT NULL,
Address nvarchar(255)NULL,
City nvarchar(25)NULL,
PostalCode nvarchar(10) NULL,
CountryCode nvarchar(3) NOT NULL,
CountryName nvarchar(60) NOT NULL,
StateCode nvarchar(8) NULL,
StateName nvarchar(60) NULL,
Phone nvarchar(30) NOT NULL,
Fax nvarchar(30) NULL,
TaxCode nvarchar(15) NULL,
ProspectID nvarchar(15) NULL,
CreateDate datetime NOT NULL,
LastUpdateDate datetime NULL)
This is the script I used to insert records for the first time
insert into SEC_ODS.dbo.ODSCustomerBase
select
--Identity as CustomerBaseID
'E4SE' as Recordsource, --could be others, eg 'BST', CRM, nvarchar(4)'
'C' as RecordType, --could be 'p'as prospects, add a case statement
ss.siteURN,
ss.sitedesc,
ss.FS_URL + 'frmcustomer.aspx?CustomerID=' + customerid as CustomerLink,
co.HomeCurrencyCode,
c.customerid,
c.currencycode as customercurrencycode,
c.entityname as CustomerName,
c.entityshortname CustomerShortName,
c.address,
c.city,
c.postalcode,
c.countrycode,
cn.countryname,
c.statecode,
s.statename,
c.phone,
c.fax,
c.taxcode,
c.prospectid,
c.createdate,
c.lastupdatedate
from country cn,
SECSite ss,
company co,
customer c
left outer join state s
on c.statecode = s.statecode
where ss.LocalSiteFlag = 1
and cn.countrycode = c.countrycode
So if I check the ODS table using this script, it gives me 4395 results
use sec_ods
go
select count(*) from ODSCustomerBase
While this is the script that I use to insert NEW RECORDS ONLY. This is where I used the where not exist condition and the sub query which doesn't work because it doubles the number of records. The goal is to insert only new records.
insert into SEC_ODS.dbo.ODSCustomerBase
select
--Identity as CustomerBaseID
'E4SE' as Recordsource, --could be others, eg 'BST', CRM, nvarchar(4)'
'C' as RecordType, --could be 'p'as prospects, add a case statement
ss.siteURN,
ss.sitedesc,
ss.FS_URL + 'frmcustomer.aspx?CustomerID=' + customerid as CustomerLink,
co.HomeCurrencyCode,
c.customerid,
c.currencycode,
c.entityname,
c.entityshortname,
c.address,
c.city,
c.postalcode,
c.countrycode,
cn.countryname,
c.statecode,
s.statename,
c.phone,
c.fax,
c.taxcode,
c.prospectid,
c.createdate,
c.lastupdatedate
from country cn,
SECSite ss,
company co,
customer c
left outer join state s
on c.statecode = s.statecode
where ss.LocalSiteFlag = 1
and cn.countrycode = c.countrycode
and not exists(select * from SEC_ODS.dbo.ODSCustomerBase b
where(Recordsource=b.Recordsource and
RecordType=b.RecordType and
ss.siteURN=b.siteURN and
ss.sitedesc=b.sitedesc and
ss.FS_URL=b.CustomerLink and
co.HomeCurrencyCode=b.HomeCurrencyCode and
c.customerid=b.customerid and
c.currencycode=b.CustomerCurrencyCode and
c.entityname=b.CustomerName and
c.entityshortname=b.CustomerShortName and
c.address=b.address and
c.city=b.city and
c.postalcode=b.postalcode and
c.countrycode=b.countrycode and
cn.countryname=b.countryname and
c.statecode=b.statecode and
s.statename=b.statename and
c.phone=b.phone and
c.fax=b.fax and
c.taxcode=b.taxcode and
c.prospectid=b.prospectid and
c.createdate=b.createdate and
c.lastupdatedate=b.lastupdatedate))
So if I check the ODS table using this script, it gives me 8790 results which is wrong.
use sec_ods
go
select count(*) from ODSCustomerBase
Can someone help me with this please?
Thank you.
Use either ISNULL or COALESCE around all the fields that could be NULL.
e.g. ISNULL(c.address,'') = ISNULL(b.address,'')
If you want to restrict the already inserted data better to go Merge Statment it will just update the existing data or insert the new data
MERGE ODSCustomerBase AS T
USING (
SELECT Col1,col2,.... FROM ODSCustomerBase
) AS S
ON (
t.Col1 = s.Col1
AND t.Col2 = s.Col2
AND .....
)
WHEN NOT MATCHED BY TARGET
THEN
INSERT (
Col1
,Col2
,....
,....
)
VALUES (
S.Col1
--,S.NrID
,s.Col2
,s.Col3
)
WHEN MATCHED
THEN
UPDATE
SET T.Col1 = S.Col1
WHEN NOT MATCHED BY SOURCE
AND EXISTS (
SELECT 1
FROM ODSCustomerBase c
WHERE t.Col1 = Col1
)
THEN
DELETE;

How do I write this crosstab type query in Linq (tables & data provided)

This is a simple sort of crosstab query. I am having difficulty transferring my SQL knowledge to Linq and because it is hard to test (I have not gotten linqpad running properly for WP7 development) I can't really "play" easily.
CREATE TABLE Store
(
StoreID INT NOT NULL,
StoreName VARCHAR(10) NOT NULL
)
INSERT INTO Store (StoreID,StoreName) VALUES (1,'Store A')
INSERT INTO Store (StoreID,StoreName) VALUES (2,'Store B')
CREATE TABLE ProductType
(
ProductTypeID INT NOT NULL,
ProductTypeName VARCHAR(20) NOT NULL
)
INSERT INTO ProductType (ProductTypeID,ProductTypeName) VALUES (1,'Clothing')
INSERT INTO ProductType (ProductTypeID,ProductTypeName) VALUES (2,'Food')
CREATE TABLE Product
(
ProductID INT NOT NULL,
ProductTypeID INT NOT NULL,
ProductName VARCHAR(20) NOT NULL
)
INSERT INTO Product (ProductID,ProductTypeID,ProductName) VALUES (1,1,'Hat')
INSERT INTO Product (ProductID,ProductTypeID,ProductName) VALUES (2,1,'Shirt')
INSERT INTO Product (ProductID,ProductTypeID,ProductName) VALUES (3,1,'Pant')
INSERT INTO Product (ProductID,ProductTypeID,ProductName) VALUES (4,2,'Orange')
INSERT INTO Product (ProductID,ProductTypeID,ProductName) VALUES (5,2,'Apple')
CREATE TABLE Purchase
(
PurchaseID INT NOT NULL,
ProductID INT NOT NULL,
StoreID INT NOT NULL,
Quantity INT NULL,
Amount INT NULL
)
INSERT INTO Purchase (PurchaseID,ProductID,StoreID,Quantity,Amount) VALUES (1,1,1,5,10)
INSERT INTO Purchase (PurchaseID,ProductID,StoreID,Quantity,Amount) VALUES (2,2,1,4,12)
INSERT INTO Purchase (PurchaseID,ProductID,StoreID,Quantity,Amount) VALUES (3,3,1,1,10)
INSERT INTO Purchase (PurchaseID,ProductID,StoreID,Quantity,Amount) VALUES (4,4,1,3,16)
INSERT INTO Purchase (PurchaseID,ProductID,StoreID,Quantity,Amount) VALUES (5,5,1,7,12)
INSERT INTO Purchase (PurchaseID,ProductID,StoreID,Quantity,Amount) VALUES (6,1,2,10,22)
INSERT INTO Purchase (PurchaseID,ProductID,StoreID,Quantity,Amount) VALUES (7,2,2,8,26)
SELECT s.StoreName
,SUM(CASE
WHEN t.ProductTypeID=1 THEN Amount
ELSE 0
END ) as ClothingAmount
,SUM(CASE
WHEN t.ProductTypeID=2 THEN Amount
ELSE 0
END )AS FoodQuantity
,SUM(CASE
WHEN t.ProductTypeID=1 THEN Quantity
ELSE 0
END ) as ClothingAmount
,SUM(CASE
WHEN t.ProductTypeID=2 THEN Quantity
ELSE 0
END )AS FoodQuantity
FROM Purchase u
INNER JOIN Product r
ON u.ProductID=r.ProductID
INNER JOIN ProductType t
ON r.ProductTypeID=t.ProductTypeID
INNER JOIN Store s
ON s.StoreID=u.StoreID
GROUP BY s.StoreName
The following is a 1:1 translation of your SQL query statement in LINQ:
from u in Purchases
join r in Products on u.ProductID equals r.ProductID
join t in ProductTypes on r.ProductTypeID equals t.ProductTypeID
join s in Stores on u.StoreID equals s.StoreID
group new {Purchase = u, Product = r, ProductType = t, Store = s} by s.StoreName into grouped
select new {
StoreName = grouped.Key,
ClothingAmount = grouped.Sum(entry => entry.ProductType.ProductTypeID == 1 ? entry.Purchase.Amount : 0),
FoodAmount = grouped.Sum(entry => entry.ProductType.ProductTypeID == 2 ? entry.Purchase.Amount : 0),
ClothingQuantity = grouped.Sum(entry => entry.ProductType.ProductTypeID == 1 ? entry.Purchase.Quantity : 0),
FoodQuantity = grouped.Sum(entry => entry.ProductType.ProductTypeID == 2 ? entry.Purchase.Quantity : 0)
}
which gets translated into the following SQL:
SELECT SUM(
(CASE
WHEN [t2].[ProductTypeID] = #p0 THEN [t0].[Amount]
ELSE #p1
END)) AS [ClothingAmount], SUM(
(CASE
WHEN [t2].[ProductTypeID] = #p2 THEN [t0].[Amount]
ELSE #p3
END)) AS [FoodAmount], SUM(
(CASE
WHEN [t2].[ProductTypeID] = #p4 THEN [t0].[Quantity]
ELSE #p5
END)) AS [ClothingQuantity], SUM(
(CASE
WHEN [t2].[ProductTypeID] = #p6 THEN [t0].[Quantity]
ELSE #p7
END)) AS [FoodQuantity], [t3].[StoreName]
FROM [Purchase] AS [t0]
INNER JOIN [Product] AS [t1] ON [t0].[ProductID] = [t1].[ProductID]
INNER JOIN [ProductType] AS [t2] ON [t1].[ProductTypeID] = [t2].[ProductTypeID]
INNER JOIN [Store] AS [t3] ON [t0].[StoreID] = [t3].[StoreID]
GROUP BY [t3].[StoreName]
Note however that it is highly recommended to set proper primary and foreign keys on the tables. Once that's done (and the Model is updated accordingly) the same query can be reduced to:
from purchase in Purchases
group purchase by purchase.Store.StoreName into grouped
select new {
StoreName = grouped.Key,
ClothingAmount = grouped.Sum(purchase => purchase.Product.ProductTypeID == 1 ? purchase.Amount : 0),
FoodAmount = grouped.Sum(purchase => purchase.Product.ProductTypeID == 2 ? purchase.Amount : 0),
ClothingQuantity = grouped.Sum(purchase => purchase.Product.ProductTypeID == 1 ? purchase.Quantity : 0),
FoodQuantity = grouped.Sum(purchase => purchase.Product.ProductTypeID == 2 ? purchase.Quantity : 0)
}