Count if or Count where in SQL - sql

I have a data set of contracts and the nationalities of people working on them. A sample is as follows.
Contract Country
GTT001 DE
GTT001 DE
GTT001 US
BFF333 US
BFF333 US
BFF333 DE
HHH222 GB
HHH222 GB
HHH222 GB
I need a query that will count the number of people working on each contract from each country. So one that will produce a table like below:
DE US GB
GTT001 2 1 0
BFF333 1 2 0
HHH222 0 0 3
I am working in Access 2010. Is there a countif or some equivalent that will allow me to count values based on conditions?

You want to use GROUP BY using both contract and then country. This will give you a list like this:
Contract Country Count
GTT001 DE 2
GTT001 US 1
BFF333 US 2
BFF333 DE 1
HHH222 GB 3
Then you want to pivot those values to get it into the format you want. The 0s will still be missing...

DECLARE #Pivotcols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #Pivotcols = STUFF((SELECT distinct N',' + QUOTENAME([Country])
from [Contract]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #Pivotcols
set #query = N'SELECT [Contract], ' + #Pivotcols + N'
from
(
SELECT [Contract]
,[Country]
FROM [TEST_DB].[dbo].[Contract]
) sourceTable
pivot
(
Count([Country])
for [Country] in (' + #Pivotcols + N')
) p '
execute sp_executesql #query;
The core query
SELECT * from
(SELECT [Contract]
,[Country]
FROM [TEST_DB].[dbo].[Contract]
) sT
pivot
(
Count([Country])
for [Country] in ([DE],[US],[GB])
) p

PIVOT will work, it's a bit more complicated but so raw sql you could GROUP BY, for example:
SELECT Contract, Country, COUNT(*)
FROM [YourTable]
GROUP BY Contract, Country
ORDER BY Country

Related

SQL Server 2012 Dynamic Pivot with a Join [duplicate]

I have a table of Customers
Customer ID Name
1 John
2 Lewis
3 Mary
I have another table CustomerRewards
TypeID Description
1 Bronze
2 Silver
3 Gold
4 Platinum
5 AnotherOne
And the final table
RewardID TypeID CustomerID
1 1 1
2 1 1
3 2 1
4 2 2
The customerTypes table is dynamic, many of these types can be added and removed. Basically all I want is the columns to be generated dynamically and a count in each, something like
CustomerName Bronze Silver Gold Platinum AnotherOne total
John 2 1 0 0 0 3
Lewis 0 1 0 0 0 1
Grand TOTAL 2 2 0 0 0 4
The problem like I said it that the types are dynamic and the customers are dynamic so I need the columns to be dynamic depending on the types in the system
I have tagged c# as I need this in a DataGridView
Thanks in advance
You will want to use a PIVOT function for this. If you have a known number of columns, then you can hard-code the values:
select name, [Bronze], [Silver], [Gold], [Platinum], [AnotherOne]
from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in ([Bronze], [Silver], [Gold], [Platinum], [AnotherOne])
) p;
See SQL Fiddle with Demo.
Now if you have an unknown number of columns, then you can use dynamic SQL to PIVOT:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(description)
from customerrewards
group by description, typeid
order by typeid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT name,' + #cols + ' from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle With Demo
If you need to include the Total column, then you can use ROLLUP (Static Version Demo):
select name, sum([Bronze]) Bronze, sum([Silver]) Silver,
sum([Gold]) Gold, sum([Platinum]) Platinum, sum([AnotherOne]) AnotherOne
from
(
select name, [Bronze], [Silver], [Gold], [Platinum], [AnotherOne]
from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in ([Bronze], [Silver], [Gold], [Platinum], [AnotherOne])
) p
) x
group by name with rollup
Dynamic version (Demo):
DECLARE #cols AS NVARCHAR(MAX),
#colsRollup AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(description)
from customerrewards
group by description, typeid
order by typeid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colsRollup
= STUFF((SELECT ', Sum(' + QUOTENAME(description) + ') as '+ QUOTENAME(description)
from customerrewards
group by description, typeid
order by typeid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'SELECT name, '+ #colsRollup + '
FROM
(
SELECT name,' + #cols + ' from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in (' + #cols + ')
) p
) x1
GROUP BY name with ROLLUP'
execute(#query)

convert vertical sql result into horizontal output

I have a table, #t with 16 rows:
id int
description varchar(60)
balance decimal(6,2)
I need the description & balance data, and "select description, balance from #t order by id" will do the job. But ideally, I could do with showing the results horizontally rather than vertically.
Now I know I can build a new table with 16 columns and populate the balance for each such column using much dynamic sql, but, I'm also sure that this can be done a good deal more easily using pivot or something like that - though I dont really understand how.
Can someone please enlighted me?
Thanks
John
Assuming you are using SQL Server you can implement the PIVOT function to convert the rows of data into columns. The basic syntax will be:
select *
from
(
select description, balance
from yourtable
) d
pivot
(
sum(balance)
for description in ([desc1], desc2]) -- replace this with the names of your descriptions
) piv;
Of course if you have an unknown number of description values, then you will need to use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(description)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ' + #cols + '
from
(
select description, balance
from yourtable
) x
pivot
(
sum(balance)
for description in (' + #cols + ')
) p '
execute sp_executesql #query

SQL Server query for grouping row as column and getting corresponding designation

I have 2 tables in SQL Server:
Table 1 : Department
DeptId Dept Name
------------------
1 Software Development
2 Testing
3 Customization
Table 2 : Designation
DesigId Desig Name DeptId
---------------------------
1 TL 1
2 PL 1
3 TestEngg 2
4 SE 3
I want the following output which takes department as column heading and group designation under the corresponding department column,
Software Development Testing Customization
TL TestEngg SE
PL
I tried with the below query but Im able to get only the Id's
DECLARE #deptcols AS VARCHAR(MAX);
DECLARE #querystr AS VARCHAR(MAX);
select #deptcols = STUFF((SELECT distinct ',' + QUOTENAME(Dept_Id)
FROM Designation
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #querystr = 'SELECT ' + #deptcols + ' from
(
select Desig_Name, Dept_Id,Desig_Id
from Designation
) p
pivot
(
count(Desig_Id) FOR Dept_Id in (' + #deptcols + ')
) pv '
execute(#querystr)
Your code was so very close. My suggestion when working with PIVOT especially a dynamic version is to write the static version first, then convert it to dynamic sql.
The static version, where you hard-code values is like this:
SELECT [Software Development], [Testing], [Customization]
from
(
select d.[Dept Name],
s.[Desig Name],
row_number() over(partition by s.deptid order by s.desigid) rn
from Designation s
left join department d
on s.[DeptId] = d.[DeptId]
) p
pivot
(
max([Desig Name])
FOR [Dept Name] in ([Software Development], [Testing], [Customization])
) pv
See SQL Fiddle with Demo. The static version allows you to be sure that the syntax is correct and all values, columns etc are in the right places.
Then once you have the syntax it is easy to convert to the dynamic SQL version:
DECLARE #deptcols AS VARCHAR(MAX)
DECLARE #querystr AS VARCHAR(MAX)
select #deptcols = STUFF((SELECT ',' + QUOTENAME([Dept Name])
FROM Department
GROUP BY [Dept Name], DeptId
ORDER BY DeptId
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #querystr =
'SELECT ' + #deptcols + ' from
(
select d.[Dept Name],
s.[Desig Name],
row_number() over(partition by s.deptid order by s.desigid) rn
from Designation s
left join department d
on s.[DeptId] = d.[DeptId]
) p
pivot
(
max([Desig Name])
FOR [Dept Name] in (' + #deptcols + ')
) pv '
execute(#querystr)
See SQL Fiddle with Demo
Both give the result:
| SOFTWARE DEVELOPMENT | TESTING | CUSTOMIZATION |
---------------------------------------------------
| TL | TestEngg | SE |
| PL | (null) | (null) |
You will notice that I added the line row_number() over(partition by s.deptid order by s.desigid) rn to the SELECT statement. This allows you to return more than one Desig Name for each Dept Name, without this you will only return one value.
I think PIVOT keyword is what you should use here. PIVOT can be used to transform datasets such that columns become rows.
No need to construct dynamic queries
Take a look at this article: http://archive.msdn.microsoft.com/SQLExamples/Wiki/View.aspx?title=PIVOTData
More info: http://msdn.microsoft.com/en-us/library/ms177410(v=sql.105).aspx

Dynamically create columns sql

I have a table of Customers
Customer ID Name
1 John
2 Lewis
3 Mary
I have another table CustomerRewards
TypeID Description
1 Bronze
2 Silver
3 Gold
4 Platinum
5 AnotherOne
And the final table
RewardID TypeID CustomerID
1 1 1
2 1 1
3 2 1
4 2 2
The customerTypes table is dynamic, many of these types can be added and removed. Basically all I want is the columns to be generated dynamically and a count in each, something like
CustomerName Bronze Silver Gold Platinum AnotherOne total
John 2 1 0 0 0 3
Lewis 0 1 0 0 0 1
Grand TOTAL 2 2 0 0 0 4
The problem like I said it that the types are dynamic and the customers are dynamic so I need the columns to be dynamic depending on the types in the system
I have tagged c# as I need this in a DataGridView
Thanks in advance
You will want to use a PIVOT function for this. If you have a known number of columns, then you can hard-code the values:
select name, [Bronze], [Silver], [Gold], [Platinum], [AnotherOne]
from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in ([Bronze], [Silver], [Gold], [Platinum], [AnotherOne])
) p;
See SQL Fiddle with Demo.
Now if you have an unknown number of columns, then you can use dynamic SQL to PIVOT:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(description)
from customerrewards
group by description, typeid
order by typeid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT name,' + #cols + ' from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle With Demo
If you need to include the Total column, then you can use ROLLUP (Static Version Demo):
select name, sum([Bronze]) Bronze, sum([Silver]) Silver,
sum([Gold]) Gold, sum([Platinum]) Platinum, sum([AnotherOne]) AnotherOne
from
(
select name, [Bronze], [Silver], [Gold], [Platinum], [AnotherOne]
from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in ([Bronze], [Silver], [Gold], [Platinum], [AnotherOne])
) p
) x
group by name with rollup
Dynamic version (Demo):
DECLARE #cols AS NVARCHAR(MAX),
#colsRollup AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(description)
from customerrewards
group by description, typeid
order by typeid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colsRollup
= STUFF((SELECT ', Sum(' + QUOTENAME(description) + ') as '+ QUOTENAME(description)
from customerrewards
group by description, typeid
order by typeid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'SELECT name, '+ #colsRollup + '
FROM
(
SELECT name,' + #cols + ' from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in (' + #cols + ')
) p
) x1
GROUP BY name with ROLLUP'
execute(#query)

Convert Rows to columns using 'Pivot' in mssql when columns are string data type

I need to know whether 'pivot' in MS SQL can be used for converting rows to columns if there is no aggregate function to be used. i saw lot of examples with aggregate function only. my fields are string data type and i need to convert this row data to column data.This is why i wrote this question.i just did it with 'case'. Can anyone help me......Thanks in advance.
You can use a PIVOT to perform this operation. When doing the PIVOT you can do it one of two ways, with a Static Pivot that you will code the rows to transform or a Dynamic Pivot which will create the list of columns at run-time:
Static Pivot (see SQL Fiddle with a Demo):
SELECT *
FROM
(
select empid, wagecode, amount
from t1
) x
pivot
(
sum(amount)
for wagecode in ([basic], [TA], [DA])
) p
Dynamic Pivot:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(wagecode)
FROM t1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT empid, ' + #cols + ' from
(
select empid, wagecode, amount
from t1
) x
pivot
(
sum(amount)
for wagecode in (' + #cols + ')
) p '
execute(#query)
Both of these will give you the same results
sample format
empid wagecode amount
1 basic 1000
1 TA 500
1 DA 500
2 Basic 1500
2 TA 750
2 DA 750
empid basic TA DA
1 1000 500 500
2 1500 750 750
THE ANSWER I GOT IS
SELECT empID , [1bas] as basic, [1tasal] as TA,[1otsal] as DA
FROM (
SELECT empID, wage, amount
FROM table) up
PIVOT (SUM(amt) FOR wgcod IN ([1bas], [1tasal],[1otsal])) AS pvt
ORDER BY empID
GO
Try this:
SELECT empid AS EmpID
, ISNULL(SUM(CASE wagecode WHEN 'basic' THEN Amount ELSE 0 END), 0) AS Basic
, ISNULL(SUM(CASE wagecode WHEN 'ta' THEN Amount ELSE 0 END), 0) AS TA
, ISNULL(SUM(CASE wagecode WHEN 'da' THEN Amount ELSE 0 END), 0) AS DA
FROM Employee
GROUP BY empid