I have two tables Contact and Invoice linked by ContactId
Please see fiddle
I am selecting all the contact who have spend more than 2500 per any year and the query works fine.
I wanted it to display in a below format.
Any help on this please using sql-server. I can easily do this using crystal-report cross tab, but trying to do thing only using sql-server
You can PIVOT, then UNPIVOT your data from your original query to get it in the desired format:
WITH T AS
( SELECT c.ContactID,
ContactName = c.Name,
Year = DATEPART(YEAR, i.InvDate),
Invoices = CAST(COUNT(i.InvoiceID) AS FLOAT),
InvTotal = CAST(SUM(i.InvTotal) AS FLOAT)
FROM Invoice AS i
INNER JOIN dbo.Contact AS c
ON c.ContactID = i.InvContactID
GROUP BY c.ContactID, c.Name, DATEPART(YEAR, i.InvDate)
HAVING SUM(i.InvTotal) > 2000
)
SELECT ContactName = CASE WHEN pvt.Measure = 'InvTotal' THEN '' ELSE pvt.ContactName END,
pvt.Measure,
[2012] = ISNULL(pvt.[2012], 0),
[2013] = ISNULL(pvt.[2013], 0),
[2014] = ISNULL(pvt.[2014], 0)
FROM T
UNPIVOT
( Value
FOR Measure IN ([Invoices], [InvTotal])
) AS upvt
PIVOT
( SUM(Value)
FOR [Year] IN ([2012], [2013], [2014])
) AS pvt
ORDER BY pvt.ContactName, Measure;
Example on SQL Fiddle
Related
I have the following query: (simplified to one year)
WITH myTable
AS (SELECT cv.Company,
cv.Zip,
DATEPART(year, od.OrderDate) AS TheYear,
'' + DATEPART(year, od.OrderDate) AS TheYear2,
od.OrderNumber AS countOrders,
STUFF(
(
SELECT '| ' + FORMAT(DateVisited, 'MM-yyyy') + ' ' + MeetingType + ' '
FROM CustomerVisits cv1
WHERE(RIGHT(od.Email, LEN(od.Email) - CHARINDEX('#', od.email))) = cv1.EmailDomain
AND cv1.Zip = od.Zip
GROUP BY MeetingType,
FORMAT(DateVisited, 'MM-yyyy')
ORDER BY FORMAT(DateVisited, 'MM-yyyy') ASC FOR XML PATH('')
), 1, 1, '') AS Meetings,
od.FinalProductTotal AS total
FROM orders od
LEFT JOIN CustomerVisits cv ON od.Zip = cv.Zip
WHERE(RIGHT(od.Email, LEN(od.Email) - CHARINDEX('#', od.email))) = cv.EmailDomain
--AND od.OrderDate > #fromDate
AND approved = 1
AND cancelled = 0
AND od.OrderDate > '01-JAN-2010')
--and the PIVOT
SELECT Company,
Zip,
Meetings,
[02020] AS [Orders - 2020],
ISNULL(CAST([2020] AS INT), 0) AS [Total - 2020],
Total = CAST((SUM(ISNULL([2020], 0))) AS INT)
FROM myTable PIVOT(COUNT(countOrders) FOR TheYear2 IN([02020])) AS myPvt PIVOT(SUM(total) FOR TheYear IN([2020])) AS myPvt2
GROUP BY Company,
zip,
Meetings,
[2020],
[02020];
For it I'm trying to add the count of orders for each year, but I'm having problems understanding how should I proceed, I might be complicating it too much, but I'm not getting the proper count
I think that the myTable table is getting properly the "countOrders" but I'm not sure if I'm doing it properly on the pivot table to group by them and show the count by year
right now it kinda seems that is counting all the orders as I needed, however when I manually check on the DB seems that the data is off.
also, something I don't understand is why is it repeating the "company" so many times when I'm doing grouping by it?
example
I ended up case instead of pivot table. this solved the issue, with format:
CAST(SUM(CASE WHEN TheYear = 2010 THEN 1 ELSE 0 END)AS INT) AS [Orders - 2010] ,
CAST(SUM(CASE WHEN TheYear = 2010 THEN Total ELSE 0 END)AS INT) AS [Total - 2010],
in case it works for someone :)
I have the following table:
PERSON
ID
Name
date_created
date_left
What I want is a list of all months and the amount of users joined and the amount of users that left.
I already have the following query: it returns the amount of new users that joined in the month that I pass:
select MONTH(date_created) 'Month', YEAR(date_created) 'Year', count(*) as 'New Users'
from person p
where YEAR(date_created) = 2018 and MONTH( p.date_created) = 5
group by MONTH(date_created), YEAR(date_created)
It returns what I want:
How would I edit this to include a year report and add the column 'Users left' next to the 'new users' one?
My result would be:
MONTH YEAR NEW USERS USERS LEFT
1 2019 10 5
I would "unpivot" the data using cross apply:
select v.[year], v.[month], sum(v.iscreated) as num_created,
sum(v.isleft) as num_left
from person p cross apply
(values (year(p.date_created), month(p.date_created), 1, 0),
(year(p.date_left), month(p.date_left), 0, 1)
) v([year], [month], iscreated, isleft)
group by v.[year], v.[month]
order by v.[year], v.[month];
The straight-forward approach is probably to full outer join all entries and all leaves. SQL Server makes this a bit awkward by not featuring USING, so we must use ON and COALESCE on month and year instead.
select
coalesce(pin.year, pout.year) as year,
coalesce(pin.month, pout.month) as month,
coalesce(pin.cnt, 0) as count_in,
coalesce(pout.cnt, 0) as count_out
from
(
select year(date_created) as year, month(date_created) as month, count(*) as cnt
from person
group by year(date_created), month(date_created)
) pin
full outer join
(
select year(date_left) as year, month(date_left) as month, count(*) as cnt
from person
group by month(date_left), year(date_left)
) pout on pout.year = pin.year and pout.month = pin.month
order by year, month;
Maybe you could do it with a SubSelect? Tried it right now with ORACLE Syntax, I'm not sure if it works in SQL-Server.
SELECT * FROM
(
select MONTH(date_created) 'Month_C', YEAR(date_created) 'Year_C', count(*) as 'New Users'
from person p
where YEAR(date_created) = 2018 and MONTH( p.date_created) = 5
group by MONTH(date_created), YEAR(date_created)
)created_user,
(
select MONTH(date_left) 'Month_L', YEAR(date_left) 'Year_L', count(*) as 'New Users'
from person p
where YEAR(date_left) = 2018 and MONTH( p.date_left) = 5
group by MONTH(date_left), YEAR(date_left)
) left_user
where created_user.Year_C = left_user.Year_L
and created_user.Month_C = left_user.Month_L
A "Case" in CRM has a field called "Status" with four options.
I'm trying to
build a report in CRM that fills a table with every week of the year (each row is a different week), and then counts the number of cases that have each Status option (the columns would be each of the Status options).
The table would look like this
Status 1 Status 2 Status 3
Week 1 3 55 4
Week 2 5 23 5
Week 3 14 11 33
So far I have the following:
SELECT
SUM(case WHEN status = 1 then 1 else 0 end) Status1,
SUM(case WHEN status = 2 then 1 else 0 end) Status2,
SUM(case WHEN status = 3 then 1 else 0 end) Status3,
SUM(case WHEN status = 4 then 1 else 0 end) Status4,
SUM(case WHEN status = 5 then 1 else 0 end) Status5
FROM [DB].[dbo].[Contact]
Which gives me the following:
Status 1 Status 2 Status 3
2 43 53
Now I need to somehow split this into 52 rows for the past year and filter these results by date (columns in the Contact table). I'm a bit new to SQL queries and CRM - any help here would be much appreciated.
Here is a SQLFiddle with my progress and sample data: http://sqlfiddle.com/#!2/85b19/1
Sounds like you want to group by a range. The trick is to create a new field that represents each range (for you one per year) and group by that.
Since it also seems like you want an infinite range of dates, marc_s has a good summary for how to do the group by trick with dates in a generic way: SQL group by frequency within a date range
So, let's break this down:
You want to make a report that shows, for each contact, a breakdown, week by week, of the number of cases registered to that contact, which is divided into three columns, one for each StateCode.
If this is the case, then you would need to have 52 date records (or so) for each contact. For calendar like requests, it's always good to have a separate calendar table that lets you query from it. Dan Guzman has a blog entry that creates a useful calendar table which I'll use in the query.
WITH WeekNumbers AS
(
SELECT
FirstDateOfWeek,
-- order by first date of week, grouping calendar year to produce week numbers
WeekNumber = row_number() OVER (PARTITION BY CalendarYear ORDER BY FirstDateOfWeek)
FROM
master.dbo.Calendar -- created from script
GROUP BY
FirstDateOfWeek,
CalendarYear
), Calendar AS
(
SELECT
WeekNumber =
(
SELECT
WeekNumber
FROM
WeekNumbers WN
WHERE
C.FirstDateOfWeek = WN.FirstDateOfWeek
),
*
FROM
master.dbo.Calendar C
WHERE
CalendarDate BETWEEN '1/1/2012' AND getutcdate()
)
SELECT
C.FullName,
----include the below if the data is necessary
--Cl.WeekNumber,
--Cl.CalendarYear,
--Cl.FirstDateOfWeek,
--Cl.LastDateOfWeek,
'Week: ' + CAST(Cl.WeekNumber AS VARCHAR(20))
+ ', Year: ' + CAST(Cl.CalendarYear AS VARCHAR(20)) WeekNumber
FROM
CRM.dbo.Contact C
-- use a cartesian join to produce a table list
CROSS JOIN
(
SELECT
DISTINCT WeekNumber,
CalendarYear,
FirstDateOfWeek,
LastDateOfWeek
FROM
Calendar
) Cl
ORDER BY
C.FullName,
Cl.WeekNumber
This is different from the solution Ben linked to because Marc's query only returns weeks where there is a matching value, whereas you may or may not want to see even the weeks where there is no activity.
Once you have your core tables of contacts split out week by week as in the above (or altered for your specific time period), you can simply add a subquery for each StateCode to see the breakdown in columns as in the final query below.
WITH WeekNumbers AS
(
SELECT
FirstDateOfWeek,
WeekNumber = row_number() OVER (PARTITION BY CalendarYear ORDER BY FirstDateOfWeek)
FROM
master.dbo.Calendar
GROUP BY
FirstDateOfWeek,
CalendarYear
), Calendar AS
(
SELECT
WeekNumber =
(
SELECT
WeekNumber
FROM
WeekNumbers WN
WHERE
C.FirstDateOfWeek = WN.FirstDateOfWeek
),
*
FROM
master.dbo.Calendar C
WHERE
CalendarDate BETWEEN '1/1/2012' AND getutcdate()
)
SELECT
C.FullName,
--Cl.WeekNumber,
--Cl.CalendarYear,
--Cl.FirstDateOfWeek,
--Cl.LastDateOfWeek,
'Week: ' + CAST(Cl.WeekNumber AS VARCHAR(20)) +', Year: ' + CAST(Cl.CalendarYear AS VARCHAR(20)) WeekNumber,
(
SELECT
count(*)
FROM
CRM.dbo.Incident I
INNER JOIN CRM.dbo.StringMap SM ON
I.StateCode = SM.AttributeValue
INNER JOIN
(
SELECT
DISTINCT ME.Name,
ME.ObjectTypeCode
FROM
CRM.MetadataSchema.Entity ME
) E ON
SM.ObjectTypeCode = E.ObjectTypeCode
WHERE
I.ModifiedOn >= Cl.FirstDateOfWeek
AND I.ModifiedOn < dateadd(day, 1, Cl.LastDateOfWeek)
AND E.Name = 'incident'
AND SM.AttributeName = 'statecode'
AND SM.LangId = 1033
AND I.CustomerId = C.ContactId
AND SM.Value = 'Active'
) ActiveCases,
(
SELECT
count(*)
FROM
CRM.dbo.Incident I
INNER JOIN CRM.dbo.StringMap SM ON
I.StateCode = SM.AttributeValue
INNER JOIN
(
SELECT
DISTINCT ME.Name,
ME.ObjectTypeCode
FROM
CRM.MetadataSchema.Entity ME
) E ON
SM.ObjectTypeCode = E.ObjectTypeCode
WHERE
I.ModifiedOn >= Cl.FirstDateOfWeek
AND I.ModifiedOn < dateadd(day, 1, Cl.LastDateOfWeek)
AND E.Name = 'incident'
AND SM.AttributeName = 'statecode'
AND SM.LangId = 1033
AND I.CustomerId = C.ContactId
AND SM.Value = 'Resolved'
) ResolvedCases,
(
SELECT
count(*)
FROM
CRM.dbo.Incident I
INNER JOIN CRM.dbo.StringMap SM ON
I.StateCode = SM.AttributeValue
INNER JOIN
(
SELECT
DISTINCT ME.Name,
ME.ObjectTypeCode
FROM
CRM.MetadataSchema.Entity ME
) E ON
SM.ObjectTypeCode = E.ObjectTypeCode
WHERE
I.ModifiedOn >= Cl.FirstDateOfWeek
AND I.ModifiedOn < dateadd(day, 1, Cl.LastDateOfWeek)
AND E.Name = 'incident'
AND SM.AttributeName = 'statecode'
AND SM.LangId = 1033
AND I.CustomerId = C.ContactId
AND SM.Value = 'Canceled'
) CancelledCases
FROM
CRM.dbo.Contact C
CROSS JOIN
(
SELECT
DISTINCT WeekNumber,
CalendarYear,
FirstDateOfWeek,
LastDateOfWeek
FROM
Calendar
) Cl
ORDER BY
C.FullName,
Cl.WeekNumber
I have a table containing the following line:
I'd like to create a view that displays the following result (without changing my original table) :
For each line having the same id,day,month and year I'd like to leave a single line with the cost and count and insert 0 in the others.
Here is a portable approach not requiring PARTITION. I have assumed you will not have the same datetimeIN value for more than one row in a group:
select t.id, t.day, t.month, t.year,
case when tm.id is null then 0 else t.cost end as cost,
case when tm.id is null then 0 else t.Count end as Count,
t.datetimeIN, t.datetimeOUT
from MyTable t
left outer join (
select id, day, month, year, min(datetimeIN) as minIN
from MyTable
group by id, day, month, year
) tm on t.id = tm.id
and t.day = tm.day
and t.month = tm.month
and t.year = tm.year
and t.datetimeIN = tm.minIN
You can do something like this:
SELECT id, day, month, year,
CASE WHEN nNum = 1 then cost else 0 end as cost,
CASE WHEN nNum = 1 then "Count" else 0 end as "Count",
datetimeIN, datetimeOUT
FROM (
SELECT id, day, month, year,
cost, "Count", datetimeIN, datetimeOUT,
row_number() OVER (PARTITION BY id, day, month, year
ORDER BY datetimeIN) as nNum
FROM TableName
) A
It uses row_number() to number the rows, and then a CASE statement to single out the first one and make it behave differently.
See it working on SQL Fiddle here.
or, using a common table expression:
with commonTableExp ([day], [month], [year], minDate) as (
select [day], [month], [year], min(datetimeIn)
from #temp
group by [day], [month], [year])
select id,
dt.[day],
dt.[month],
dt.[year],
case when datetimein = minDate then [cost] else 0 end,
case when datetimein = minDate then [count] else 0 end,
dateTimeIn
from #temp dt join commonTableExp cte on
dt.[day] = cte.[day] and
dt.[month] = cte.[month] and
dt.[year] = cte.[year]
order by dateTimeIn
Query
Select id, [day], [month], [year], Case When K.RowID = 1 Then [cost] Else 0 End as Cost, Case When K.RowID = 1 Then [count] Else 0 End as [count], [DateTimeIN], [DateTimeOut] From
(
select ROW_NUMBER() Over(Partition by id, [day], [month], [year] Order by ID ) as RowID, * From Testing
)K
Drop table Testing
Click here to see SQL Profiler details for Red Filter's Query
Click here to see SQL Profiler details for my Query
For More Information You can see SQL Fiddle
Hi i have a query which i need to show the number of transactions a user made,per day with the EUR equivalent of each transaction.
The query below does do that (find the eur equivalent by getting an average rate) but because the currencies are different i get the results by currency instead and not by total. what the query returns is:
Numb Transactions,Date, userid,transaction_type,total value (per currency),eur_equiv
1 12/12, 2, test 5 10
2 12/12,2, test 2 2
whereas i want it to return
Numb Transactions,Date, userid,transaction_type,total value (per currency),eur_equiv
1 12/12, 2, test 7 12
the query is shown below
SELECT COUNT(DISTINCT(ot.ID)) AS 'TRANSACTION COUNTER'
,CONVERT(VARCHAR(10) ,ot.CREATED_ON ,103) AS [DD/MM/YYYY]
,lad.ci
,ot.TRA_TYPE
,c.C_CODE
,CASE
WHEN op.CURRENCY_ID='CURRENCY-002' THEN SUM(CAST(op.IT_AMOUNT AS MONEY))
/(
SELECT AVG(CAST(cr.B_RATE AS MONEY)) AS AVG_RATE
FROM C_RATE cr
WHERE cr.CURRENCY_ID = 'CURRENCY-002'
)
WHEN op.CURRENCY_ID='-CURRENCY-005' THEN SUM(CAST(op.IT_AMOUNT AS MONEY))
/(
SELECT AVG(CAST(cr.B_RATE AS MONEY)) AS AVG_RATE
FROM C_RATE cr
WHERE cr.CURRENCY_ID = 'CURRENCY-005'
)
WHEN op.CURRENCY_ID='CURRENCY-006' THEN SUM(CAST(op.IT_AMOUNT AS MONEY))
/(
SELECT AVG(CAST(cr.B_RATE AS MONEY)) AS AVG_RATE
FROM C_RATE cr
WHERE cr.CURRENCY_ID = 'CURRENCY-006'
)
ELSE '0'
END AS EUR_EQUIVAL
FROM TRANSACTION ot
INNER JOIN PAYMENT op
ON op.ID = ot.ID
INNER JOIN CURRENCY c
ON op.CURRENCY_ID = c.ID
INNER JOIN ACCOUNT a
ON a.ID = ot.ACCOUNT_ID
INNER JOIN ACCOUNT_DETAIL lad
ON lad.A_NUMBER = a.A_NUMBER
INNER JOIN CUST cus
ON lad.CI = cus.CI
WHERE ot.TRA_TYPE_ID IN ('INBANK-TYPE'
,'IN-AC-TYPE'
,'DOM-TRANS-TYPE')
AND ot.STATUS_ID = 'COMPLETED'
AND cus.BRANCH IN ('123'
,'456'
,'789'
,'789')
GROUP BY
lad.CI
,CONVERT(VARCHAR(10) ,ot.CREATED_ON ,103)
,c.C_CODE
,op.CURRENCY_ID
,ot.TRAN_TYPE_ID
HAVING SUM(CAST(op.IT_AMOUNT AS MONEY))>'250000.00'
ORDER BY
CONVERT(VARCHAR(10) ,ot.CREATED_ON ,103) ASC
SELECT MIN([Numb Transactions]
, Date
, UserID
, Transaction_type
, SUM([Total Value]
, SUM([Eur Equiv]
FROM (
... -- Your current select (without order by)
) q
GROUP BY
Date
, UserId
, Transaction_type
The problem resides most likely in a double row in your joins. What I do, is Select * first, and see what columns generate double rows. You might need to adjust a JOIN relationship for the double rows to disappear.
Without any resultsets, it's very hard to reproduce the error you are getting.
Check for the following things:
Select * returns only double rows on the data i will merge with an aggregate function. If the answer here is "NO", you will need to alter a JOIN relationship with a Subselect. I am thinking of that Account and Account detail table.
certain joins can create duplicate rows if the join cannot be unique enough. Maybe you will have to join on multiple things here, for example JOIN table1 ON table1.ID = table2.EXT_ID and table1.Contact = table2.Contact
Couple of things:
1) Consider using a function for:
SELECT AVG(CAST(cr.B_RATE AS MONEY)) AS AVG_RATE
FROM C_RATE cr
WHERE cr.CURRENCY_ID = 'CURRENCY-002' with the currencyID as a parameter
2) Would grouping sets work here?
3) was the sum on the case or on the individual whens?
sum(CASE ..) vs sum(cast(op.IT_Amount as money)