Difference between rows with the same value - sql

I have the table
create table fct_bonus (
date timestamp not null,
type varchar(10) not null,
amount numeric(19,2) not null,
userid varchar(30) not null
)
type can be IN or OUT, amount is always >0
I need to find sums of ins and outs for userid 123 on date 2016-08-01', and also the balans, which should be count as all ins minus all outs of userid123.
I use the query
select distinct userid, type, sum(amount)
from fct_bonus
where userid = 123 and date <= '2016-08-01'
group by type
but I don't know, how to count the balans. Please, help.

This would seem to do what you are describing:
select userid,
sum(case when type = 'IN' then 1 else 0 end) as ins,
sum(case when type = 'OUT' then 1 else 0 end) as outs,
sum(case when type = 'IN' then amount when type = 'OUT' then - amount end) as balance
from fct_bonus
where userid = 123 and date <= '2016-08-01'
group by userid;

Related

MSSQL Group by and Select rows from grouping

I'm trying to figure out if what I'm trying to do is possible. Instead of resorting to multiple queries on a table, I wanted to group the records by business date and id then group by the id and select one date for a field and another date for the other field.
SELECT
*
{AMOUNT FROM DATE}
{AMOUNT FROM OTHER DATE}
FROM (
SELECT
date,
id,
SUM(amount) AS amount
FROM
table
GROUP BY id, date
AS subquery
GROUP BY id
It seems that you're looking to do a pivot query. I usually use cross tabs for this. Based on the query you posted, it could look like:
SELECT
id,
SUM(CASE WHEN date = '20190901' THEN amount ELSE 0 END) AmountFromSept01,
SUM(CASE WHEN date = '20191001' THEN amount ELSE 0 END) AmountFromOct01
FROM (
SELECT
date,
id,
SUM(amount) AS amount
FROM
table
GROUP BY id, date
)AS subquery
GROUP BY id;
You could also use a CTE.
WITH CTE AS(
SELECT
date,
id,
SUM(amount) AS amount
FROM
table
GROUP BY id, date
)
SELECT
id,
SUM(CASE WHEN date = '20190901' THEN amount ELSE 0 END) AmountFromSept01,
SUM(CASE WHEN date = '20191001' THEN amount ELSE 0 END) AmountFromOct01
FROM CTE
GROUP BY id;
Or even be a rebel and do the operation directly.
SELECT
id,
SUM(CASE WHEN date = '20190901' THEN amount ELSE 0 END) AmountFromSept01,
SUM(CASE WHEN date = '20191001' THEN amount ELSE 0 END) AmountFromOct01
FROM CTE
GROUP BY id;
However, some people have tested for performance and found that pre-aggregating can improve performance.
If I understand you correctly, then you're just trying to pivot, but only with two particular dates:
select id,
date1 = sum(iif(date = '2000-01-01', amount, null)),
date2 = sum(iif(date = '2000-01-02', amount, null))
from [table]
group by id

How I can group by and count in PostgreSQL to prevent empty cells in result

I have the table in PostgreSQL DB
Need to calculate SUM of counts for each event_type (example for 4 and 1)
When I use query like this
SELECT account_id, date,
CASE
WHEN event_type = 1 THEN SUM(count)
ELSE null
END AS shows,
CASE
WHEN event_type = 4 THEN SUM(count)
ELSE null
END AS clicks
FROM widgetstatdaily WHERE account_id = 272 AND event_type = 1 OR event_type = 4 GROUP BY account_id, date, event_type ORDER BY date
I receive this table
With <null> fields. It's because I have event_type in select and I need to GROUP BY on it.
How I can make query to receive grouped by account_id and date result without null's in cells? Like (first row)
272 2018-03-28 00:00:00.000000 57 2
May be I can group it after receiving result
You need conditional aggregation and some other fixes. Try this:
SELECT account_id, date,
SUM(CASE WHEN event_type = 1 THEN count END) as shows,
SUM(CASE WHEN event_type = 4 THEN count END) as clicks
FROM widgetstatdaily
WHERE account_id = 272 AND
event_type IN (1, 4)
GROUP BY account_id, date
ORDER BY date;
Notes:
The CASE expression should be an argument to the SUM().
The ELSE NULL is redundant. The default without an ELSE is NULL.
The logic in the WHERE clause is probably not what you intend. That is fixed using IN.
try its
SELECT account_id, date,
SUM(CASE WHEN event_type = 1 THEN count else 0 END) as shows,
SUM(CASE WHEN event_type = 4 THEN count else 0 END) as clicks
FROM widgetstatdaily
WHERE account_id = 272 AND
event_type IN (1, 4)
GROUP BY account_id, date
ORDER BY date;

Sql ISNULL condition in Sql Pivot and Sql case

I searched for many solutions on SO and elsewhere but couldn't quite understand how to write a query for my problem.
Anyway my query looks like below
SELECT * FROM
(
SELECT Id, Date, Name, Amount,
CASE
WHEN DATEDIFF(DAY,Date,GETDATE()) <=0
THEN 'Current'
WHEN DATEDIFF(DAY,Date,GETDATE()) <30
THEN 'Due30'
WHEN DATEDIFF(DAY,Date,GETDATE()) <60
THEN 'Due60'
ELSE 'Due90'
END AS [Age]
FROM Statement
WHERE (Amount <> 0)
) AS S
PIVOT
(
SUM(Amount)
FOR[Age] IN ([Current],[Due30],[Due60],[Due90])
) P
and the result looks like this
Id Date Name Current Due30 Due60 Due90
----------- ---------- --------------------------------------------
1 2016-04-03 Alan NULL NULL NULL 110.00
2 2016-05-02 TC NULL NULL 30.00 NULL
where should i insert IsNull condition to be able to remove the null in the result and add a zero there.
I tried inserting IsNull in the pivot query but we all know that is not meant to work
You have to add it repetitively in the final SELECT, when you replace the SELECT * (which should only exist in ad-hoc queries or EXISTS tests) with the column list:
SELECT
Id,
Date,
Name,
COALESCE([Current],0) as [Current],
COALESCE(Due30,0) as Due30,
COALESCE(Due60,0) as Due60,
COALESCE(Due90,0) as Due90
FROM
(
SELECT Id, Date, Name, Amount,
CASE
WHEN DATEDIFF(DAY,Date,GETDATE()) <=0
THEN 'Current'
WHEN DATEDIFF(DAY,Date,GETDATE()) <30
THEN 'Due30'
WHEN DATEDIFF(DAY,Date,GETDATE()) <60
THEN 'Due60'
ELSE 'Due90'
END AS [Age]
FROM Statement
WHERE (Amount <> 0)
) AS S
PIVOT
(
SUM(Amount)
FOR[Age] IN ([Current],[Due30],[Due60],[Due90])
) P
I've also used COALESCE since it's generally the preferred option (ANSI standard, extends to more than two arguments, applies normal type precedence rules) instead of ISNULL.
SELECT Id
, [Date]
, Name
, [Current] = SUM(CASE WHEN val <= 0 THEN Amount ELSE 0 END)
, Due30 = SUM(CASE WHEN val < 30 THEN Amount ELSE 0 END)
, Due60 = SUM(CASE WHEN val < 60 THEN Amount ELSE 0 END)
, Due90 = SUM(CASE WHEN val >= 60 THEN Amount ELSE 0 END)
FROM dbo.[Statement] t
CROSS APPLY (
SELECT val = DATEDIFF(DAY, [Date], GETDATE())
) s
WHERE Amount <> 0
GROUP BY Id, [Date], Name

query for making matrix style .rdl report using report wizard

I need help writing the query for a matrix style report.
My data is in the following format
id body_part incident_type1 incident_type2 incident_type3
1 head PPE null null
2 ankle Unsafe Act Facility null
3 hand null null null
4 head Facility PPE Unsafe Act
I want the rows to be the body parts and columns to be incident types. If incident_type1 is null, then I want a count in a "n/a" column. But, if incident_type2 and/or 3 is null, I do not want those to count in the "n/a" column.
Facility Unsafe Act PPE N/A
ankle 1 1 0 0
hand 0 0 0 1
head 1 1 2 0
Here's one way of doing this:
select body_part
, Facility = sum(case when incident_type1 = 'Facility' or incident_type2 = 'Facility' or incident_type3 = 'Facility' then 1 else 0 end)
, [Unsafe Act] = sum(case when incident_type1 = 'Unsafe Act' or incident_type2 = 'Unsafe Act' or incident_type3 = 'Unsafe Act' then 1 else 0 end)
, PPE = sum(case when incident_type1 = 'PPE' or incident_type2 = 'PPE' or incident_type3 = 'PPE' then 1 else 0 end)
, [N/A] = sum(case when incident_type1 is null then 1 else 0 end)
from Incidents
group by body_part
order by body_part
SQL Fiddle with demo.
This assumes known incident types and that the same row will not have the same incident type multiple times.
I was able to get this working by creating a stored procedure where I inserted the data into a temp table. I was then able to use the report wizard with "EXEC SP_Name" as the query. Then I selected Body_part as my rows, Incident_type as my columns, and Totals as my data.
CREATE TABLE #tmp
(
Body_part VARCHAR(200) NOT NULL,
Incident_type VARCHAR(250) NOT NULL,
)
INSERT INTO #tmp
SELECT ISNULL(Body_part, 'N/A'), ISNULL(Incident_type, 'N/A')
FROM [safety].vwIncomingPINS
WHERE submitted_on >= dateadd(year,-1,getdate()) AND submitted_on <=getdate()
INSERT INTO #tmp
SELECT ISNULL(Body_part, 'N/A'), Incident_type2
FROM [safety].vwIncomingPINS
WHERE submitted_on >= dateadd(year,-1,getdate()) AND submitted_on <=getdate() AND Incident_type2 IS NOT NULL
INSERT INTO #tmp
SELECT ISNULL(Body_part, 'N/A'), Incident_type3
FROM [safety].vwIncomingPINS
WHERE submitted_on >= dateadd(year,-1,getdate()) AND submitted_on <=getdate() AND Incident_type3 IS NOT NULL
SELECT Body_part, Incident_type, count(*) AS Totals from #tmp
GROUP BY Body_part, Incident_type

Multiple Queries in different table

(Also posted here.)
So I have two tables, one is invalid table and the other is valid table.
valid table:
id
status
date
invalid table:
id
status
date
I have to produce a report with this output:
date on-time late total valid invalid1 invalid2 total rate
--------- ------- ---- ----- ----- -------- -------- ----- ----
9/10/2011 4 10 14 3 3 3 6
date: common fields on the 2 tables, field to group by, how many records on that day has
on-time: count of all the id on the valid table
late: count of all the records(id) on the invalid table
total: total of on-time and late
valid: count of id on the valid table with the "valid" status
invalid1: count of id on the invalid table with "invalid1" status
invalid2: count of id on the invalid table with "invalid2" status
total: total of valid, invalid1, invalid2
rate: average of totals
It's basically multiple queries with different table. How can I achieve it?
Someting like this?
SELECT
*,
(result.total + result._total) / 2 AS rate
FROM (
SELECT
date,
SUM(CASE WHEN data.valid = 1 THEN 1 ELSE 0 END) AS ontime,
SUM(CASE WHEN data.valid = 0 THEN 1 ELSE 0 END) AS late,
COUNT(*) AS total,
SUM(CASE WHEN data.valid = 1 AND data.status = 'valid' THEN 1 ELSE 0 END) AS valid,
SUM(CASE WHEN data.valid = 0 AND data.status = 'invalid1' THEN 1 ELSE 0 END) AS invalid1,
SUM(CASE WHEN data.valid = 0 AND data.status = 'invalid2' THEN 1 ELSE 0 END) AS invalid2,
SUM(CASE WHEN data.status IN ('valid', 'invalid', 'invalid2') THEN 1 ELSE 0 END) AS _total
FROM (
SELECT
date,
status,
valid = 1
FROM
Valid
UNION ALL
SELECT
date,
status,
valid = 0
FROM
InValid ) AS data
GROUP BY
date) AS result
SELECT date, ontime, late, ontime+late total, valid, invalid1, invalid2, valid+invalid1+invalid2 total
FROM
(SELECT date,
COUNT(*) late,
COUNT(IIF(status = 'invalid1', 1, NULL)) invalid1,
COUNT(IIF(status = 'invalid2', 1, NULL)) invalid2,
FROM invalid
GROUP BY date
) JOIN (
SELECT date,
COUNT(*) ontime,
COUNT(IIF(status = 'valud', 1, NULL)) valid,
FROM valid
GROUP BY date
) USING (date)
First of all, it seems that you are holding exactly the same information in 2 tables - I would recommend merging those tables together and add an additional boolean column called valid to hold the info related to validity of the record.
The query on your existent DB structure might look something like this:
SELECT unioned.* FROM (
( SELECT v.date AS date, v.status AS status, v.id AS id, COUNT(id) AS valid, 0 AS invalid1, 0 AS invalid2 FROM valid v GROUP BY v.date)
UNION
( SELECT i1.date AS date, i1.status AS status, i1.id AS id, 0 AS valid, COUNT(i1.id) AS invalid1, 0 AS invalid2 FROM invalid1 i1 GROUP BY i1.date)
UNION
( SELECT i2.date AS date, i2.status AS status, i2.id AS id, 0 AS valid, 0 AS invalid1, COUNT(i.id) AS invalid2 FROM invalid1 i1 GROUP BY i1.date)
) AS unioned GROUP BY unioned.date