I have been assigned to get the data in required format from two tables.
TableStaff :
StaffID | Staff Name
--------+-----------
1 | John
2 | Jack
and TableLead
LeadID | LeadValue | LeadStaus | StaffID
-------+-----------+-----------+--------
1 | 5000 | New | 1
2 | 8000 | Qualified | 1
3 | 3000 | New | 2
As you will notice StaffID is a foreign key referencing TableStaff.
I have to represent the data in following format
StaffID | StaffName | NewLeadCount | QualifiedLeadCount
--------+-----------+--------------+-------------------
1 | John | 1 | 1
2 | Jack | 1 | 0
What I have tried till now is :
SELECT
COUNT([LeadID ]) AS LdCount, 'New' AS StageName
FROM
[dbo].[TableLead]
WHERE
[LeadStaus] = 'New'
UNION
SELECT
COUNT([LeadID ]) AS LdCount, 'Qualified' AS StageName
FROM
[dbo].[TableLead]
WHERE
[LeadStaus] = 'Qualified '
Any NULL spots should be replaced by 0. Can anyone show me the right direction to approach the problem ?
I would recommend conditional aggregation:
select s.staffid, s.staffname,
sum(case when l.leadstatus = 'New' then 1 else 0 end) as newLeadCount,
sum(case when l.leadstatus = 'Qualified' then 1 else 0 end) as qualifiedLeadCount
from TableStaff s
inner join TableLead l on l.staffid = s.staffid
group by s.staffid, s.staffname
I have two tables
1) Document: which represent a document
+----+----------+------+
| ID | Body | Type |
+----+----------+------+
| 1 | Ramesh | 1 |
| 2 | Khilan | 1 |
| 3 | kaushik | 4 |
| 4 | Chaitali | 2 |
| 5 | Hardik | 2 |
+----+----------+------+
2) Destination: which represent a party of the document
+--------+------------+--------+
| UserId | DocumentId | Status |
+--------+------------+--------+
| 6 | 3 | 4 |
| 4 | 5 | 5 |
| 89 | 2 | 0 |
| 15 | 4 | 3 |
| 89 | 1 | 0 |
+--------+------------+--------+
The status column represent a folder for the user, i want to get the count for each type for each folder, even if the folder is empty for a specifi user,
however if want them in this from,
+--------+--------+--------------+--------------+--------------+
| UserId | Status | Type 1 Count | Type 2 Count | Type 4 Count |
+--------+--------+--------------+--------------+--------------+
| 89 | 0 | 2 | 0 | 0 |
| 89 | 3 | 0 | 0 | 0 |
| 89 | 4 | 0 | 0 | 0 |
| 89 | 5 | 0 | 0 | 0 |
+--------+--------+--------------+--------------+--------------+
the issue I'm facing is I can't find a way to get the types the user does not have by join, i can get them using CASE but not in the form i want
my query is:
`SELECT dd.[Status],
SUM(CASE WHEN d.[Type] = 1 THEN 1 ELSE 0 END) AS 'Type1Count'
SUM(CASE WHEN d.[Type] = 2 THEN 1 ELSE 0 END) AS 'Type2Count'
SUM(CASE WHEN d.[Type] = 4 THEN 1 ELSE 0 END) AS 'Type4Count'
FROM [User] u LEFT JOIN [Destination] dd ON u.[Id] = dd.[UserId]
LEFT JOIN [Document] d ON dd.[DocumentId] = d.[Id]
WHERE u.[Id] = #UserId`
the result is
+--------+--------+--------------+--------------+--------------+
| UserId | Status | Type 1 Count | Type 2 Count | Type 4 Count |
+--------+--------+--------------+--------------+--------------+
| 89 | 0 | 2 | 0 | 0 |
+--------+--------+--------------+--------------+--------------+
So join all users onto a table of all statuses (I have named this Folder as per you description in the question) before you then join to Document and Destination:
SELECT u.UserId, st.Status,
SUM(CASE WHEN doc.Type = 1 THEN 1 ELSE 0 END) AS [Type 1 Count],
SUM(CASE WHEN doc.Type = 2 THEN 1 ELSE 0 END) AS [Type 2 Count],
SUM(CASE WHEN doc.Type = 4 THEN 1 ELSE 0 END) AS [Type 4 Count]
FROM User u
CROSS JOIN Folder st
LEFT OUTER JOIN Destination d
ON d.UserId = u.UserId
AND d.Status = st.Status
LEFT OUTER JOIN Document doc
ON doc.ID = d.DocumentId
GROUP BY u.UserId, st.Status
ORDER BY u.UserId
I am currently having difficulty getting the correct values from my table. Here is my table
NOTE: The column Status has 3 possible values (Cleaned, Unclean, Closed)
+-----------+-------------+--------+------------+
|ApplicantID|ApplicantName| Status | HireDate |
+-----------+-------------+--------+------------+
| 1 | John Smith |Cleaned |08/26/2015 |
| 2 | Alex Murphy |Closed |09/12/2015 |
| 3 | Oliver David|Cleaned |01/11/2015 |
| 4 | Max Payne |Unclean |03/18/2015 |
+-----------+-------------+--------+------------+
The output I'm expecting and it should also be sorted by year.
For example I call all these records for the year 2015 which I get using the variable #Year.
NOTE: The column Total is the SUM of Cleaned and Unclean
+---------+-----------+-----------+----------+---------+
| Month | Cleaned | Unclean | Closed | Total |
+---------+-----------+-----------+----------+---------+
| January| 1 | 0 | 0 | 1 |
| February| 0 | 0 | 0 | 0 |
| March | 0 | 1 | 0 | 1 |
| April | 0 | 0 | 0 | 0 |
| May | 0 | 0 | 0 | 0 |
| June | 0 | 0 | 0 | 0 |
| July | 0 | 0 | 0 | 0 |
| August | 1 | 0 | 0 | 1 |
|September| 0 | 0 | 1 | 0 |
| October| 0 | 0 | 0 | 0 |
| November| 0 | 0 | 0 | 0 |
| December| 0 | 0 | 0 | 0 |
+---------+-----------+-----------+----------+---------+
I can't seem to get the right code, for the sql this is my current code.
SELECT Month(HireDate) AS Month, COUNT(*)
FROM Hires
GROUP BY Month(HireDate)
I know my coding is wrong, because it is incomplete.
Generate a list of numbers from 1 to 12 first to hold all months. Then do a LEFT JOIN on Hires to make sure all missing months are accounted for. Then use conditional aggregation for the totals:
SQL Fiddle
;WITH CteMonths AS(
SELECT * FROM(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)t(N)
)
SELECT
Month = DATENAME(MONTH, DATEADD(MONTH, N-1,0)),
Cleaned = SUM(CASE WHEN h.Status = 'Cleaned' THEN 1 ELSE 0 END),
Closed = SUM(CASE WHEN h.Status = 'Closed' THEN 1 ELSE 0 END),
Unclean = SUM(CASE WHEN h.Status = 'Unclean' THEN 1 ELSE 0 END),
Total = SUM(CASE WHEN h.Status IN('Cleaned', 'Unclean') THEN 1 ELSE 0 END)
FROM CteMonths m
LEFT JOIN Hires h
ON m.N = MONTH(h.HireDate)
--AND YEAR(h.HireDate) = #year --uncomment this line to filter for year.
GROUP BY m.N
ORDER BY m.N
If you want to include the YEAR:
SQL Fiddle
;WITH CteMonths AS(
SELECT * FROM(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)t(N)
),
CteYears(yr) AS(
SELECT DISTINCT YEAR(HireDate) FROM Hires
),
CteAllDates(dt) AS(
SELECT
DATEADD(MONTH, m.N - 1, DATEADD(YEAR, y.yr - 1900, 0))
FROM CteMonths m
CROSS JOIN CteYears y
)
SELECT
Year = YEAR(d.dt),
Month = DATENAME(MONTH, d.dt),
Cleaned = SUM(CASE WHEN h.Status = 'Cleaned' THEN 1 ELSE 0 END),
Closed = SUM(CASE WHEN h.Status = 'Closed' THEN 1 ELSE 0 END),
Unclean = SUM(CASE WHEN h.Status = 'Unclean' THEN 1 ELSE 0 END),
Total = SUM(CASE WHEN h.Status IN('Cleaned', 'Unclean') THEN 1 ELSE 0 END)
FROM CteAllDates d
LEFT JOIN Hires h
ON MONTH(d.dt) = MONTH(h.HireDate)
AND YEAR(d.dt) = YEAR(h.HireDate)
GROUP BY YEAR(d.dt), MONTH(d.dt), DATENAME(MONTH, d.dt)
ORDER BY YEAR(d.dt), MONTH(d.dt)
If you want to filter for year, say #year = 2015, you can replace the previous ctes with:
;WITH CteMonths AS(
SELECT * FROM(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)t(N)
),
CteAllDates(dt) AS(
SELECT
DATEADD(MONTH, m.N - 1, DATEADD(YEAR, #year - 1900, 0))
FROM CteMonths m
)...
I suggest to create TEMP table with values from 1 to 12 (numbers of months) and JOIN your table with TEMP table. To achieve values as columns names you can use PIVOT or CASE. You can do It in following:
INSERT INTO #Months VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
SELECT DATENAME(MONTH, DATEADD(MONTH, m.Id-1, 0)) AS [Month]
, SUM(CASE WHEN [Status] = 'Cleaned' THEN 1 ELSE 0 END) AS [Cleaned]
, SUM(CASE WHEN [Status] = 'Closed' THEN 1 ELSE 0 END ) AS [Closed]
, SUM(CASE WHEN [Status] = 'Unclean' THEN 1 ELSE 0 END) AS [Unclean]
, SUM(CASE WHEN [Status] IN ('Unclean', 'Cleaned') THEN 1 ELSE 0 END) AS [Total]
FROM #Test t
RIGHT JOIN #Months m ON m.Id = MONTH(t.HireDate)
GROUP BY m.Id
OUTPUT
+---------+-----------+-----------+----------+---------+
| Month | Cleaned | Unclean | Closed | Total |
+---------+-----------+-----------+----------+---------+
| January | 1 | 0 | 0 | 1 |
| February| 0 | 0 | 0 | 0 |
| March | 0 | 1 | 0 | 1 |
| April | 0 | 0 | 0 | 0 |
| May | 0 | 0 | 0 | 0 |
| June | 0 | 0 | 0 | 0 |
| July | 0 | 0 | 0 | 0 |
| August | 1 | 0 | 0 | 1 |
|September| 0 | 0 | 1 | 0 |
| October | 0 | 0 | 0 | 0 |
| November| 0 | 0 | 0 | 0 |
| December| 0 | 0 | 0 | 0 |
+---------+-----------+-----------+----------+---------+
DEMO
You can test It at: SQL FIDDLE
I have a table which I have simplified to that below. It actually extends up to Type60
Number | Type1 | Type2 | Type3 | Type4
------------------------------------------------------
1 | 1 | 1 | 1 | 0
2 | 1 | 1 | 1 | 0
3 | 1 | 1 | 1 | 0
4 | 0 | 2 | 0 | 1
5 | 0 | 2 | 1 | 0
6 | 0 | 2 | 0 | 1
7 | 1 | 1 | 1 | 0
8 | 1 | 1 | 0 | 1
9 | 1 | 2 | 1 | 0
10 | 0 | 2 | 0 | 1
The output that I want, is like below (extended to Name60)
Name | Value | CountOfValue
-----------------------------------
Name1 | 0 | 4
Name1 | 1 | 6
Name2 | 1 | 5
Name2 | 2 | 5
Name3 | 0 | 6
Name3 | 1 | 4
Name4 | 0 | 4
Name4 | 1 | 6
To get there, I have been doing:
select 'Name1' as Name, Type1, Count(Number)
from table1
group by Type1
union
select 'Name2' as Name, Type2, Count(Number)
from table1
group by Type2
union
select 'Name3' as Name, Type3, Count(Number)
from table1
group by Type3
union
select 'Name4' as Name, Type4, Count(Number)
from table1
group by Type4
upto Name60 etc
this take a fair while to run,
Is there a more efficent (faster) way to do the same thing does anyone know?
Thanks, Gary
You could try UNPIVOT.
SELECT Name, [values] AS Value, Count(*) AS CountOfValue
FROM
(
SELECT REPLACE([Type], 'Type', 'Name') AS Name, [values]
FROM #testPivot
UNPIVOT ([values] FOR [type] IN
(Type1, Type2, Type3, Type4) -- ETC to Type60
) AS unpvt
) AS test
GROUP BY Name, [values]
ORDER BY Name, Value
You'd still have to put in all the Type1 to Type 60 in the UNPIVOT. Check out PIVOT and UNPIVOT.
I am looking for a solution for the following problem, which affects two tables. I already tried to search for the solution, but couldn't find the way to go.
single_value
| docId | siteNo | siteName | siteAccount | comment | docDate | extNo
---+------------+--------+---------------+-------------+---------+-------------------------+-------
1 | T000000095 | 201060 | Main Location | 92400 | NULL | 2014-10-31 00:00:00.000 | NULL
multi_value
| docId | field_no | row_no | value_char | value_date | value_num
---+------------+----------+--------+------------+------------+-----------
1 | T000000095 | 60 | 1 | NULL | NULL | 250.00
2 | T000000095 | 60 | 2 | NULL | NULL | -1.24
3 | T000000095 | 61 | 1 | Positive | NULL | NULL
4 | T000000095 | 61 | 2 | Negative | NULL | NULL
5 | T000000095 | 62 | 1 | NULL | NULL | 90000.00
6 | T000000095 | 62 | 2 | NULL | NULL | 688000.00
What I need is now an SQL statement which gives me an output like the following one for each row_no of the table multi_value for a specific docId:
| docId | siteNo | siteName | siteAccount | comment | docDate | extNo | amount | addInfo | costUnit
---+------------+--------+---------------+-------------+---------+-------------------------+-------|--------+----------+----------
1 | T000000095 | 201060 | Main Location | 92400 | NULL | 2014-10-31 00:00:00.000 | NULL | 250.00 | Positive | 90000.00
2 | T000000095 | 201060 | Main Location | 92400 | NULL | 2014-10-31 00:00:00.000 | NULL | -1.24 | Negative | 688000.00
It has to list all Information of the table 'single_value' and kind of transpose the values of the table 'multi_value'. The connection between both tables can be achieved via the docId.
The table 'multi_value' is designed in that way, that each field no only allows one specific column to be filled:
60 = value_num (amount)
61 = value_char (addInfo)
62 = value_num (costUnit)
What is the easiest way to achieve that? The table layout cannot be changed. For the transpose of the multi_value I already tried the following, but it doesn't work with the varchar datatype within the column value_char.
SELECT row_no
SUM(case when field_no = 60 then value_num else 0 end) as amount,
--(case when field_no = 61 then value_char else 0 end) as addInfo,
SUM(case when field_no = 62 then value_num else 0 end) as costUnit
FROM multi_value
WHERE docId = 'T000000095'
GROUP By
row_no
Many thanks in advance.
Thomas
Not very elegant, but works fine :)
SELECT row_no,
SUM(case when field_no = 60 then value_num else 0 end) as amount,
case SUM(case
when field_no = 61 then (
case value_char
when 'Positive' then 1
else 2 end
) else 0 end
) when 1 then 'Positive' else 'Negative' end as addInfo,
SUM(case when field_no = 62 then value_num else 0 end) as costUnit
FROM multi_value
GROUP By
row_no
UPDATE
WITH dist as
(
SELECT DISTINCT value_char
FROM multi_value
WHERE value_char is not null
)
, with_no as
(
select value_char, row_number() over(order by value_char) [no]
from dist
)
, [raw] as
(
SELECT m.row_no,
SUM(case when m.field_no = 60 then m.value_num else 0 end) as amount,
SUM(case when m.field_no = 61 then d.[no] else 0 end) as addInfo,
SUM(case when m.field_no = 62 then m.value_num else 0 end) as costUnit
FROM multi_value m
LEFT JOIN with_no as d on d.value_char = m.value_char
GROUP By
m.row_no
)
SELECT row_no
, amount
, d.value_char as addInfo
, costUnit
FROM [raw] r
LEFT JOIN with_no as d on d.[no] = r.addInfo