SQL pivot get products by orderNr - sql

I have 2 tables:
'OrderHeader' with columns OrderNr and OrderContact.
'OrderDetail with columns OrderNr and OrderSerial.
For each ordernr there are max 3 orderserials.
So, what I'm trying to get is a table with columns:
OrderNr
OderContact
OrderSerial1
OrderSerial2
OrderSerial3
I'm now stuck with my pivot
SELECT OrderNr,
OderContact,
[1] as OrderSerial1,
[2] as OrderSerial2,
[3] as OrderSerial3
FROM
(select H.OrderNR as OrderNr,
OrderContact as OderContact,
OrderSerialsNr
from OrderHeader H inner join OrderDetail D on H.OrderNr = D.OrderNr
) AS PivSource
PIVOT
(count(OrderSerialsNr) for OrderSerialsNr in([1],[2],[3])) as pvt
I used the adventureworks DB for this.
USE [AdventureWorks2008R2]
GO
create view [test].[OrderHeader] (OrderNr,OrderContact)
as
SELECT SalesOrderID,
FirstName + ' ' + LastName
FROM Sales.SalesOrderHeader INNER JOIN
Sales.Customer ON Sales.SalesOrderHeader.CustomerID = Sales.Customer.CustomerID INNER JOIN
Person.Person ON Sales.Customer.PersonID = Person.Person.BusinessEntityID
GO
CREATE view [test].[OrderDetail] (OrderNr,OrderSerialsNr,Price)
as
SELECT [SalesOrderID]
,[ProductID]
,[unitPrice]
FROM [AdventureWorks2008R2].[Sales].[SalesOrderDetail]
where SalesOrderID in (select SalesOrderID
from [AdventureWorks2008R2].[Sales].[SalesOrderDetail]
group by SalesOrderID
having COUNT(SalesOrderID) < 4)
GO
Please help cause I don't know where to look anymore :).

To get data in pivot table the in claus should have correct data from table
you can get more idea from this query
if 1 and 2 are not in your table it will not appear as count and at a column value
CREATE TABLE #OrderHeader
(ORDERNR INT,
ORDERCONTACT CHAR(5))
CREATE TABLE #ORDERDETAIL
(ORDERNR INT,
ORDERSERIAL VARCHAR(10)
)
INSERT INTO #OrderHeader VALUES ( 1, 'X')
INSERT INTO #ORDERDETAIL VALUES ( 1, 'X1')
INSERT INTO #ORDERDETAIL VALUES ( 1, 'X2')
INSERT INTO #ORDERDETAIL VALUES ( 1, 1)
INSERT INTO #ORDERDETAIL VALUES ( 1, 2)
SELECT * FROM #OrderHeader
SELECT * FROM #ORDERDETAIL
SELECT OrderNr,
OderContact,
[1] as OrderSerial1,
[2] as OrderSerial2,
[3] as OrderSerial3
FROM
(select H.OrderNR as OrderNr,
OrderContact as OderContact,
ORDERSERIAL
from #OrderHeader H inner join #ORDERDETAIL D on H.OrderNr = D.OrderNr
) AS PivSource
PIVOT
(count(ORDERSERIAL) for ORDERSERIAL in([1],[2],[3])) as pvt
DROP TABLE #OrderHeader
DROP TABLE #ORDERDETAIL

Related

SQL Insert row when a cell does not contain a specific set of characters

My database has a column named Group.
This Group can be one of two values:
Group101 = Main group
Group101D1 = Subgroup
Every group has two options like this.
But I have some situations where Group101D1 exists but Group101 does not.
Now I want to create an insert where I search for groups with D1 that doesn't have a main group. for example I have Group105D1 but don't have Group105. I want an insert to create a row with Group105.
This is as far as I have come:
INSERT INTO (Group)
SELECT [Table1].[Group], [Table2].[Group]
FROM [Table] Table1
INNER JOIN [Table] Table2 ON [table1].[Group] = [Table2].[Group]
-- WHERE [Table2].[Group] LIKE '%D1'
-- AND [Table1].[Group] NOT LIKE '%D1'
Can some of you please help, I don't know how to finish this.
I know I probably need to use inner join, replace and a where not clause.
You will need replace Also query should exclude rows already exists
Sample data:
DECLARE #groups TABLE ([Group] VARCHAR(100), description VARCHAR(100))
INSERT #groups ([Group], description)
SELECT 'Group101', 'Something1'
UNION ALL
SELECT 'Group101d1', 'Something1'
UNION ALL
SELECT 'Group105d1', 'description_Bleh'
UNION ALL
SELECT 'Group2054', 'desc_2054'
UNION ALL
SELECT 'Group2054d1', 'desc_2054'
Use replace function and exclude Maingroup if it exists
SELECT Replace([group], 'd1', '') [Group], description
FROM #groups
WHERE [Group] NOT IN (
SELECT p.[Group]
FROM #groups g
INNER JOIN #groups p
ON g.[Group] = replace(p.[Group], 'd1', '')
)
Result:
Group description
Group105 description_Bleh
If all subgroups ends with "D1" you can use below query to insert missing main groups.
INSERT INTO (Group)
SELECT
left(subtable.Group,(len(subtable.Group)-len('D1')))
FROM
[Table1].[Group] subtable
where
charindex ( 'D1', subtable.Group) > 0 -- if it is sub-record
and
not exists --check if main group exists
(SELECT
1
FROM
[Table1].[Group] main
where (charindex ( main.Group+'D1', subtable.Group) != 0)
)
Stuff you have already just packaged with data
DECLARE #groups TABLE (grp VARCHAR(100), description VARCHAR(100));
INSERT #groups (grp, description) values
('Group101', 'Something1')
, ('Group101d1', 'Something1')
, ('Group105d1', 'description_Bleh')
, ('Group106d1', 'description_Bleh6')
, ('Group2054', 'desc_2054')
, ('Group2054d1', 'desc_2054');
select * from #groups order by grp;
insert into #groups
select replace(g1.grp, 'd1', ''), g1.description
from #groups g1
where g1.grp like '%d1'
and not exists ( select 1
from #groups g2
where g2.Grp = replace(g1.grp, 'd1', '')
);
select * from #groups order by grp;
You can use NOT EXISTS and REPLACE to get the desired results like below :
INSERT INTO Table2(Group)
select replace([Group], 'd1', '') from Table1 a
where [Group] like '%d1' and
not exists(
select 1 from Table1 where [group] = replace(a.[Group], 'd1', '')
)
I did it this way in the end!
insert into Table (Group, Description)
select replace(Group,'D1','') , description from Table
where Group like '%D1'
and replace(Group,'D1','') not in (
select Group from Table
where Group like 'Group%' and Group not like '%D1')

Get hierarchical data is SQL SERVER with fallback logic

Consider the below schema
dbo.Cultures (Id, CultureCode, ParentId)
Culture table stores the data in the parent-child relationship.
Suppose we have below demo data
5 es-ES 3
Now I have another table which stores the multilingual data for the different cultures.
Schema for the table is as following
dbo.LangData(KeyName, CultureId, Value)
here cultureId is the foreign key of dbo.Cultures table.
Suppose this table has following data
Now I require to fetch the data for all the cultures which are in the Culture table and the corresponding value column in the LangData table.
The culture Ids which are not in the LangData table, for those the Value column will the value of the corresponding parent culture Id columns value. I.e. Data will be retrieved using fallback logic
E.g. For the above values the Result set will be following.
5 es-ES Colour_IN
Here for de-DE data is missing in LangData so it's value will be the data in it's parent culture i.e. en-IN, if in case data also not found in en-IN then it will pick the data of it's parent en-US.
Tried Soloution
First I fetch the culture hierarchy using CTE
CREATE FUNCTION [dbo].[ufnGetCultureHierarchyAll] ()
RETURNS #hierarchyResult TABLE(RowNo INT, CultureId INT, ParentCultureId INT)
AS
BEGIN
WITH CultureHierarchy_CTE(RowNo, CultureId, ParentCultureId)
AS (
SELECT 1,
Id,
ParentId
FROM [dbo].Cultures
UNION ALL
SELECT RowNo + 1,
ou.Id,
ou.ParentId
FROM [dbo].Cultures ou
JOIN CultureHierarchy_CTE cte
ON ou.Id = cte.ParentCultureId
)
-- inserting desired records into table and returning
INSERT INTO #hierarchyResult (RowNo,CultureId,ParentCultureId )
SELECT RowNo, CultureId , ParentCultureId FROM CultureHierarchy_CTE
RETURN;
END
This will return the hierarchy of the all the cultures
Now I attempted to apply join of the result set with the LangData table,
DECLARE #cultureHierarchy AS TABLE(
RowNumber INT,
CultureId INT,
ParentCultureId INT
)
--SELECT * FROM master.Cultures
----Get and store culture hierarchy
INSERT INTO #cultureHierarchy
SELECT RowNo, CultureId, ParentCultureId
FROM ufnGetCultureHierarchyAll()
SELECT c.Code AS [CultureCode],
c.CultureId AS [CultureId],
rv.Value
FROM dbo.LangData rv WITH (NOLOCK)
JOIN #cultureHierarchy c ON rv.CultureId = c.CultureId
END
but it is not working.
Is someone have any Idea regarding same.
Solution using Itzik Ben-Gan's hierarchy model. If you can extend the dbo.Cultures table with Hierarchy, Lvl and Root columns and index on Hierarchy, query will be faster. It has to be rewrited in that case though.
drop table if exists dbo.Cultures;
create table dbo.Cultures (
ID int
, Code varchar(50)
, ParentID int
);
insert into dbo.Cultures (ID, Code, ParentID)
values (1, 'en-US', null), (2, 'en-IN', 1), (3, 'de-DE', 2), (4, 'hi-HI', 2)
drop table if exists dbo.LangData;
create table dbo.LangData (
KeyName varchar(100)
, CultureID int
, Value varchar(100)
);
insert into dbo.LangData (KeyName, CultureID, Value)
values ('lblColourName', 1, 'Color'), ('lblColourName', 2, 'Colour-IN');
with cteCultures as (
select
c.ID, c.Code, c.ParentID, 0 as Lvl
, convert(varchar(max), '.' + CONVERT(varchar(50), c.ID) + '.') as Hierarchy
, c.ID as Root
from dbo.Cultures c
where c.ParentID is null
union all
select
c.ID, c.Code, c.ParentID, cc.Lvl + 1 as Lvl
, cc.Hierarchy + convert(varchar(50), c.ID) + '.' as Hierarchy
, cc.Root as Root
from dbo.Cultures c
inner join cteCultures cc on c.ParentID = cc.ID
)
select
ccr.ID
, ccr.Code
, coalesce(ld.Value, ld2.Value) as Value
from cteCultures ccr
left join dbo.LangData ld on ccr.ID = ld.CultureID
outer apply (
select
top (1) tcc.ID
from cteCultures tcc
inner join dbo.LangData tld on tcc.ID = tld.CultureID
where ld.KeyName is null
and ccr.Hierarchy like tcc.Hierarchy + '%'
and ccr.Hierarchy <> tcc.Hierarchy
order by tcc.Lvl desc
) tt
left join dbo.LangData ld2 on tt.ID = ld2.CultureID
If I understand your question:
We just build your hierarchy (SEQ and Lvl are optional) and then perform TWO left joins in concert with a Coalesce().
Example
Declare #Cultures table (id int,ParentId int,Code varchar(50))
Insert into #Cultures values
( 1, NULL,'en-US')
,( 2, 1 ,'en-IN')
,( 3, 2 ,'de-DE')
,( 4, 2 ,'hi-HI')
Declare #LangData table (keyName varchar(50),CultureId int,Value varchar(50))
Insert Into #LangData values
('lblColourName',1,'Color')
,('lblColourName',2,'Color_IN')
;with cteP as (
Select Seq = cast(10000+Row_Number() over (Order by Code) as varchar(500))
,ID
,ParentId
,Lvl=1
,Code
From #Cultures
Where ParentId is null
Union All
Select Seq = cast(concat(p.Seq,'.',10000+Row_Number() over (Order by r.Code)) as varchar(500))
,r.ID
,r.ParentId
,p.Lvl+1
,r.Code
From #Cultures r
Join cteP p on r.ParentId = p.ID)
Select CultureId = A.ID
,A.Code
,Value = Coalesce(C.Value,B.Value)
From cteP A
Left Join #LangData B on (A.ParentId=B.CultureId)
Left Join #LangData C on (A.Id=C.CultureId)
Order By Seq
Returns
CultureId Code Value
1 en-US Color
2 en-IN Color_IN
3 de-DE Color_IN
4 hi-HI Color_IN

Query different type data in to a single row with the master record

I just created the following sample data to demonstrate what I am after.
I need to query the above table to get my information as 1 single row
i.e. like the following
I was planning to create two temp tables and insert the two different types of addresses seperately. Then, inner join them with the main company table. I am not sure, this is a good solution. I appreaciate if anyone share their thoughts or code to my problem.
Try this..
Select companyId,CompanyName,homesddress1
,homeaddress2,HomePostCode,OfficeAddress1,OfficeAddress2,OfficePostCode
From tblCompany a
Outer apply ( select address1 homesddress1, address2 homeaddress2,postcode HomePostCode
From tblAddress t
Where AddressType='home' and t.companyid=a.companyid)
Outer apply (select address1 OfficeAddress1, address2 Officeaddress2,postcode OfficePostCode
From tblAddress t2
Where AddressType='Office ' and t2.companyid=a.companyid)
You can do it with a simple select using two outer joins. Note that you need the joins to be outer because for some companies you may only have one address.
DECLARE #company TABLE (
CompanyId int,
CompanyName varchar(50)
)
DECLARE #companyAddress TABLE (
Id int,
AddressType varchar(10),
Address1 varchar(50),
Address2 varchar(50),
Postcode varchar(10),
CompanyId int
)
INSERT INTO #company VALUES (1, 'Test Company')
INSERT INTO #companyAddress VALUES (1, 'Home', '25 Street', 'City 1', 'BA3 1PE', 1)
INSERT INTO #companyAddress VALUES (2, 'Office', '25 Street', 'City 2', 'NA1 4TW', 1)
SELECT c.CompanyId, c.CompanyName,
h.Address1 AS HomeAddress1, h.Address2 AS HomeAddress2, h.Postcode AS HomePostcode,
o.Address1 AS OfficeAddress1, o.Address2 AS OfficeAddress2, o.Postcode AS OfficePostcode
FROM #company c
LEFT JOIN
#companyAddress h ON h.CompanyId = c.CompanyId AND h.AddressType = 'Home'
LEFT JOIN
#companyAddress o ON o.CompanyId = c.CompanyId AND o.AddressType = 'Office'
Here is an Almost Dynamic version. You just have to specify the field list in the final pivot
Declare #YourTable table (ID int,AddressType varchar(25),Address1 varchar(50),Address2 varchar(50),CompanyID int)
Insert Into #YourTable values
(1,'Home' ,'25 Street','City 1',1),
(2,'Office','10 Avenue','City 2',1)
Declare #XML xml = (Select * from #YourTable for XML RAW) --<<< Initial Query
;with cteBase as (
Select ID = R.value('#CompanyID','int') --<<< Key ID
,AddressType = R.value('#AddressType','varchar(50)')
,Item = R.value('#AddressType','varchar(50)')+Attr.value('local-name(.)','varchar(100)')
,Value = Attr.value('.','varchar(max)')
From #XML.nodes('/row') as A(R)
Cross Apply A.r.nodes('./#*[local-name(.)!="CompanyID"]') as B(Attr) --<<< Key ID
),cteDist as (Select Distinct ID,Item from cteBase
),cteComp as (
Select A.*,B.Value
From cteDist A
Cross Apply (Select Value=Stuff((Select Distinct ',' + Value
From cteBase
Where ID=A.ID
and Item=A.Item
For XML Path ('')),1,1,'') ) B
)
Select *
From (Select * From cteComp) as s
Pivot (max(value)
For Item in (HomeID,HomeAddressType,HomeAddress1,HomeAddress2,OfficeID,OfficeAddressType,OfficeAddress1,OfficeAddress2)) as pvt
Returns
ID HomeID HomeAddressType HomeAddress1 HomeAddress2 OfficeID OfficeAddressType OfficeAddress1 OfficeAddress2
1 1 Home 25 Street City 1 2 Office 10 Avenue City 2

Insert IF record EXISTS in one table and not EXISTS in other

Problem with IF EXISTS
I have two tables
create table #Table1
(
DateID date, Shop int, MAC int, Stock int, Transit int
)
INSERT INTO #Table1 values ('01.01.2014', 1, 2, 2,3)
INSERT INTO #Table1 values ('01.04.2014', 1, 2, 2,3)`
create table #Table2
(
DateID date, Shop int, MAC int, OnHand int
)
INSERT INTO #Table2 values ('01.01.2014', 1, 2, 2)
INSERT INTO #Table2 values ('01.01.2014', 2, 3, 1)
INSERT INTO #Table2 values ('01.01.2014', 3, 1, 4)
INSERT INTO #Table2 values ('01.02.2014', 1, 2, 5)
Then I need to do insert into Table 1 if it does not exist in Table 1 DateID, Shop , MAC, but exists in Table 2
I have this statement
DECLARE #Date date = '01.02.2014'
IF EXISTS (SELECT a.DateID, a.Shop, a.MAC
FROM #Table1 a
INNER JOIN #Table2 b ON a.Shop = b.Shop
AND a.MAC = b.MAC
AND a.DateID = b.DateID
WHERE a.DateID = #Date)
INSERT INTO #Table1 (DateID, Shop, MAC, Stock)
select
#Date, a.Shop, a.MAC , sum (OnHand)
from
#Table2 a
where
DateID <= #Date
group by
a.Shop, a.MAC
order by
1
other IF
else
It's working but because it has
IF EXISTS
it's always true, if I am using
IF NOT EXISTS
it's always false and this part never execute.
Please any help!
You can do it in an sql statement instead of an if statement:
insert into #Table1
(DateID, Shop, MAC, Stock)
select #Date, a.Shop, a.MAC , sum(OnHand)
from #Table2 a
left join #Table1 b
ON a.Shop = b.Shop
AND a.MAC = b.MAC
AND a.DateID = b.DateID
where b.Shop is null
group by a.Shop, a.MAC
order by 1
If you need an IF stmt - do something like this:
set #RowsToInsert = 0
select #RowsToInsert = count(*)
from #Table2 a
left join #Table1 b
ON a.Shop = b.Shop AND a.MAC = b.MAC AND a.DateID = b.DateID
where b.Shop is null
IF #RowsToInsert > 0 ... you will need to put insert logic here
IF #RowsToInsert = 0 ... no rows are missing, put update or other logic here
If you'd like to use EXISTS then this might work:
INSERT INTO #Table1 (DateID, Shop, MAC, Stock)
SELECT #Date,
A.Shop,
A.MAC ,
SUM (OnHand)
FROM #Table2 A
WHERE NOT EXISTS
(
SELECT 1
FROM #Table1 B
WHERE A.Shop = B.Shop AND
A.MAC = B.MAC AND
A.DateID = B.DateID
)
AND
A.DateID <= #Date
GROUP BY
A.Shop,
A.MAC
I fixed it by creating NONCLUSTERED INDEX and set IGNORE_DUP_KEY = ON
create table #Table1 (
DateID date, Shop int, MAC int, Stock int, Transit int )
CREATE UNIQUE NONCLUSTERED INDEX IX_RecvID ON #F_STOCK (DateID,Shop ,MAC) WITH (IGNORE_DUP_KEY = ON)

How to pivot column values of the below table?

TableA:
Brand Product
------------------
A X
A XX
A XXX
B Y
B YY
C Z
I need data as shown in Table below:
A B C
-------------------
X Y Z
XX YY NULL
XXX NULL NULL
How to do that in Sql Server 2008 ?
I dont beleive a PIVOT is what you are looking for here.
From what I can see you are looking at using the entries in order to generate the rows?
Also, PIVOTs make use of aggregate functions, so I cant see this happening.
What you can try, is something like
DECLARE #Table TABLE(
Brand VARCHAR(10),
Product VARCHAR(10)
)
INSERT INTO #Table SELECT 'A','X '
INSERT INTO #Table SELECT 'A','XX'
INSERT INTO #Table SELECT 'A','XXX'
INSERT INTO #Table SELECT 'B','Y'
INSERT INTO #Table SELECT 'B','YY'
INSERT INTO #Table SELECT 'C','Z'
;WITH Vals AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY Brand ORDER BY (SELECT NULL)) RID
FROM #Table
)
, RIDs AS (
SELECT DISTINCT
RID
FROM Vals
)
SELECT vA.Product [A],
vB.Product [B],
vC.Product [C]
FROM RIDs r LEFT JOIN
Vals vA ON r.RID = vA.RID
AND vA.Brand = 'A' LEFT JOIN
Vals vB ON r.RID = vB.RID
AND vB.Brand = 'B' LEFT JOIN
Vals vC ON r.RID = vC.RID
AND vC.Brand = 'C'
I know it is a late entry, but here is a different approach to solve it:
DECLARE #Table TABLE(Brand VARCHAR(10), Product VARCHAR(10))
INSERT INTO #Table SELECT 'A','X '
INSERT INTO #Table SELECT 'A','XX'
INSERT INTO #Table SELECT 'A','XXX'
INSERT INTO #Table SELECT 'B','Y'
INSERT INTO #Table SELECT 'B','YY'
INSERT INTO #Table SELECT 'C','Z'
SELECT [A],[B],[C] FROM (
SELECT row_number() over (partition by brand order by product) rn,
Product, brand FROM #table
) as p
PIVOT(
MAX(product) for Brand in ([A],[B],[C])
)as pvt