SQL Pivot only select rows - sql-server

I am attempting to pivot a database so that only certain rows become columns. Below is what my table looks like:
ID QType CharV NumV
1 AccNum 10
1 EmpNam John Inc 0
1 UW Josh 0
2 AccNum 11
2 EmpNam CBS 0
2 UW Dan 0
I would like the table to look like this:
ID AccNum EmpNam
1 10 John Inc
2 11 CBS
I have two main problems I am trying to account for.
1st: the value that I am trying to get isn't always in the same column. So while AccNum is always in the NumV column, EmpName is always in the CharV column.
2nd: I need to find a way to ignore data that I don't want. In this example it would be the row with UW in the QType column.
Below is the code that I have:
SELECT *
FROM testTable
Pivot(
MAX(NumV)
FOR[QType]
In ([AccNum],[TheValue])
)p
But it's giving me the below result:
ID CharV AccNum TheValue
1 10 NULL
2 11 NULL
2 CBS NULL NULL
2 Dan NULL NULL
1 John IncNULL NULL
1 Josh NULL NULL

In this case grouping with conditional aggregation should work. Try something like:
SELECT ID
, MAX(CASE WHEN QType = 'AccNum' THEN NumV END) AS AccNum
, MAX(CASE WHEN QType = 'EmpNam' THEN CharV END) AS EmpNam
FROM testTable
GROUP BY ID
Since the inner CASE only gets a value when the WHEN condition is met, the MAX function will give you the value desired. This of course, only works as long as there are only unique QTypes per ID.
Generally using PIVOT in Sql-Server doesn't work in one step when your conditions are complex, specially when you need values from different columns. You could pivot your table in two queries and join those, but it would perform poorly and is less readable than my suggestion.

Related

How can I always return Null for a column without updating the column's value in the database?

ID Name tuition num of courses
1 Brandon 4430 6
2 Lisa 2300 3
3 Victoria null 0
4 Jack 3330 4
The type of the tuition column is money, but I need to return return null in my select statement without updating the values in the table.
I tried nullif(tuition is not null), but it didn't work.
How can I return results like those in the table below, without updating the table or modifying the data in database?
ID Name tuition num of courses
1 Brandon null 6
2 Lisa null 3
3 Victoria null 0
4 Jack null 4
If you are returning null for every row, just code the column as:
NULL AS Tuition
Example query:
SELECT Id, Name, NULL as Tuition, NumCourses FROM TheTable
I have created the table and inserted records as you have shown above
It is a self join query.
-- To make sure that the underlying table is not updated run both the queries together.
select TT.Id, TT.Name,
nullif(TT.Tuition, BT.Tuition) as Tuition, TT.NOCs
from tblTuition TT
join tblTuition BT
on TT.Id = Bt.Id
select * from tblTuition
Whenever you need to get value as null then you can use like this,
SELECT NULL AS ABC FROM MYTABLE
So above statement add one ABC column in your select list AS All NULL Values, same thing can be use as getting a Default value e.g. if you want to get 1 then simply use SELECT 1 AS ABC FROM MYTABLE

Creating a recursive CTE with no rootrecord

My Apologies for the appalling Title, I was trying to be descriptive but not sure I got to the point. Hopefully the below will explain it
I begin with a table that has the following information
Party Id Party Name Party Code Parent Id
1 Acme 1 ACME1 1
2 Acme 2 ACME2 1
3 Acme 3 ACME3 3
4 Acme 4 ACME4 4
5 Acme 5 ACME5 4
6 Acme 6 ACME6 6
As you can see this isn't perfect for a recursive CTE because rather than having a NULL where there isn't a parent record it is instead parented to itself (see rows 1,3 and 6). Some however are parented normally.
I have therefore tried to amend this table in a CTE then refer to the output of that CTE as part of my recursive query... This doesn't appear to be running very well (no errors yet) so I wonder if I have managed to create an infinite loop or some other error that just slows the query to a crawl rather than killing it
My Code is below... please pick it apart!
--This is my attempt to 'clean' the data and set records parented to themselves as the 'anchor'
--record
WITH Parties
AS
(Select CASE
WHEN Cur_Parent_Id = Party_Id THEN NULL
ELSE Cur_Parent_Id
END AS Act_Parent_Id
, Party_Id
, CUR_PARTY_CODE
, CUR_PARTY_NAME
FROM EDW..TBDIMD_PARTIES
WHERE CUR_FLG = 1),
--In this CTE I referred to my 'clean' records from above and then traverse through them
--looking at the actual parent record identified
linkedParties
AS
(
Select Act_Parent_Id, Party_Id, CUR_PARTY_CODE, CUR_PARTY_NAME, 0 AS LEVEL
FROM Parties
WHERE Act_Parent_Id IS NULL
UNION ALL
Select p.Act_Parent_Id, p.Party_Id, p.CUR_PARTY_CODE, p.CUR_PARTY_NAME, Level + 1
FROM Parties p
inner join
linkedParties t on p.Act_Parent_Id = t.Party_Id
)
Select *
FROM linkedParties
Order By Level
From the data I supplied earlier the results I would expect are;
Party Id Party Name Party Code Parent Id Level
1 Acme 1 ACME1 1 0
3 Acme 3 ACME3 3 0
4 Acme 4 ACME4 4 0
6 Acme 6 ACME6 6 0
2 Acme 2 ACME2 1 1
5 Acme 5 ACME5 4 1
If everything seems to be OK then I'll assume its just a processing issue and start investigating that but I am not entirely comfortable with CTE's so wish to make sure the error is not mine before looking elsewhere.
Many Thanks
I think that you made it more complicated than it needs to be :).
drop table #temp
GO
select
*
into #temp
from (
select '1','Acme 1','ACME1','1' union all
select '2','Acme 2','ACME2','1' union all
select '3','Acme 3','ACME3','3' union all
select '4','Acme 4','ACME4','4' union all
select '5','Acme 5','ACME5','4' union all
select '6','Acme 6','ACME6','6'
) x ([Party Id],[Party Name],[Party Code],[Parent Id])
GO
;with cte as (
select
*,
[Level] = 0
from #temp
where 1=1
and [Party Id]=[Parent Id] --assuming these are root records
union all
select
t.*,
[Level] = c.[Level]+1
from #temp t
join cte c
on t.[Parent Id]=c.[Party Id]
where 1=1
and t.[Party Id]<>t.[Parent Id] --prevent matching root records with themselves creating infinite recursion
)
select
*
from cte
(* should ofcourse be replaced with actual column names)

group by with 'pre-defined row'

Say I have to following PaymentTransaction Table:
ID Amount PayMethodID
----------------------------
10254 100 1
15789 150 1
15790 200 0
16954 300 0
17864 400 1
19364 500 1
PayMethodID Desc
----------------------------
0 CASH
1 VISA
2 MASTER
3 AMEX
4 ETC
I can simply use a group by to group the PayMethodID under 1 and 0.
What i am trying to do is to show also the non-exist PayMethodID under GROUP BY
My current result with simple group by statement is
PayMethodID TotalAmount
-------------------------
0 500
1 1150
Expected result (to show 0 if its not exits in the transaction table):
PayMethodID TotalAmount
-------------------------
0 500
1 1150
2 0
3 0
4 0
This might be a simple and duplicated question, but i just cant find the keyword to search around. I would remove this post if you can find me any duplication. Thanks.
You can use LEFT JOIN, so all rows from leftmost table (TableA) will be shown whether it has a matching values on the other table or not.
SELECT a.PayMethodID,
TotalAmount = ISNULL(SUM(b.Amount), 0)
FROM TableA AS a -- <== contains list of card type
LEFT JOIN TableB AS b -- <== contains the payment list
ON a.PayMethodID = b.PayMethodID
GROUP BY a.PayMethodID
A regular OUTER (LEFT) JOIN will give you all rows from the PayMethod table no matter if they exist in the PaymentTransaction table, the rest of the sums being NULL. You can then use a COALESCE to make the null rows zero;
SELECT pm.PayMethodID, COALESCE(SUM(pt.Amount), 0) TotalAmount
FROM PayMethod pm
LEFT JOIN PaymentTransaction pt
ON pm.PayMethodID = pt.PayMethodID
GROUP BY pm.PayMethodID
An SQLfiddle to test with.

Selecting counts with conditions and grouping

Some easy points for somebody here, take the following table of data:
EventCode ProcessId
---------- ---------
2 1
-6 3
42 1
-6 2
-12 2
23 4
4 2
-23 1
12 3
-26 1
I need a query that will get be a count of all Process ID where there is a negative event code. So from the dataset above the result would be 3 (Process ID's 1,2 and 3 have negative event codes, process ID 4 does not)
Probably really simple, involving groups but I just can't see the wood for the trees.
As a relevant aside, there are millions of rows in this table.
SELECT COUNT(DISTINCT ProcessID) FROM table
WHERE EventCode < 0
In case there are multiple negative values with the same ProcessId:
SELECT COUNT(DISTINCT ProcessId) FROM table WHERE EventCode < 0;
If you want the counts grouped by ProcessId then this:
SELECT COUNT(*), [ProcessId]
FROM TBL
WHERE [EventCode] < 0
GROUP BY [ProcessId]
If you want the entire negative count:
SELECT COUNT(DISTINCT([ProcessId])) FROM Tbl WITH (NOLOCK) WHERE [EventCode] < 0
For performance:
Create a filtered non-clustered index on column EventCode and include column ProcessId, where EventCode < 0.
The quick answer is
SELECT COUNT(DISTINCT t.ProcessId)
FROM mytable t
WHERE t.EventCode < 0
AND t.ProcessId IS NOT NULL
(It's not necessary to include the predicate on ProcessId; I do here, to point out that COUNT won't include a NULL value.)
That's the simplest approach, but not the only one.
The performance of other possible queries is really going to depend on the organization of the table (HEAP or CLUSTERED), and what indexes are available.
Hope you mean that:
select count(*) from (YourTableNameHere) where EventCode < 0

ssis merge join more than 2 data sets

I'm working on an ssis package to fix some data from a table. The table looks something like this:
CustID FieldID INT_VAL DEC_VAL VARCHAR_VAL DATE_VAL
1 1 23
1 2 500.0
1 3 David
1 4 4/1/05
1 5 52369871
2 1 25
2 2 896.23
2 3 Allan
2 4 9/20/03
2 5 52369872
I want to transform it into this:
CustID FirstName AccountNumber Age JoinDate Balance
1 David 52369871 23 4/1/05 500.0
2 Allan 52369872 25 9/20/03 896.23
Currently, I've got my SSIS package set up to pull in the data from the source table, does a conditional split on the field id, then generates a derived column on each split. The part I'm stuck on is joining the data back together. I want to join the data back together on the CustId.
However, the join merge only allows you to join 2 datasets, in the end I will need to join about 30 data sets. Is there a good way to do that without having to have a bunch of merge joins?
That seems a bit awkward, why not just do it in a query?
select
CustID,
max(case when FieldID = 3 then VARCHAR_VAL else null end) as 'FirstName',
max(case when FieldID = 5 then INT_VAL else null end) as 'AccountNumber',
max(case when FieldID = 1 then INT_VAL else null end) as 'Age',
max(case when FieldID = 4 then DATE_VAL else null end) as 'JoinDate',
max(case when FieldID = 2 then DEC_VAL else null end) as 'Balance'
from
dbo.StagingTable
group by
CustID
If your source system is MSSQL, then you can use that query from SSIS or even create a view in the source database (if you're allowed to). If not, then copy the data directly to a staging table in MSSQL and query it from there.

Resources