how to double count column which already counted - sql

Please help me to solve the problem
My real table is:
id group numberOfLevel(counted column)
1 10 4
2 10 2
3 11 2
4 11 1
5 11 3
6 11 2
7 21 1
8 21 2
9 30 1
10 40 2
But i want to show:
group 1st_level 2nd_level 3rd_level over4th_level
10 0 1 0 1
11 1 2 1 0
21 1 1 0 0
30 1 0 0 0
40 0 1 0 0
Which way do i need to use to show the table?
Please share experience ?

This is a basic pivot query, an ANSII SQL case expession can be used in such a query,
and it should work on most databases:
select group_nr,
sum( case when numberOfLevel = 1 then 1 else 0 end ) As level_1st,
sum( case when numberOfLevel = 2 then 1 else 0 end ) As level_2nd,
sum( case when numberOfLevel = 3 then 1 else 0 end ) As level_3rd,
sum( case when numberOfLevel >= 4 then 1 else 0 end ) As over4th_level
from table1
group by group_nr
;
demo: http://sqlfiddle.com/#!2/4bd04/4
Don't use group as a column name, because group is a keyword in SQL.

Related

Select first date in which an event happen for each id

I have a series of Ids, some of them activate a product on certain month and that product remains activated for an X period of time, while others do not activate the product.
I want to create a column which indicates in which month the user activates the product or a NULL if the user doesn't activate it.
I've tried using a partition like the following:
SELECT id, fl_testdrive, month_dt,
CASE WHEN fl_testdrive = 1 then min(month_dt) OVER(PARTITION BY id ORDER BY month_dt ROWS UNBOUNDED PRECEDING) else 0 end as month_testdrive
FROM Table_1
However, when I try this solution, in the column month_testdrive, I do not obtain the first month in which the user appears, indepently of if he/she activated that product in that month or on a later one.
This is what I get with my query
Id flag_testdrive month_dt month_testdrive
1 0 1 1
1 0 2 1
1 1 3 1
1 1 4 1
2 0 2 0
2 0 3 0
3 1 4 4
3 1 5 4
What I'd expect:
Id flag_testdrive month_dt month_testdrive
1 0 1 3
1 0 2 3
1 1 3 3
1 1 4 3
2 0 2 0
2 0 3 0
3 1 4 4
3 1 5 4
This solution is a second best but is also fine:
Id flag_testdrive month_dt month_testdrive
1 0 1 0
1 0 2 0
1 1 3 3
1 1 4 3
2 0 2 0
2 0 3 0
3 1 4 4
3 1 5 4
You want CASE expression inside MIN():
MIN(CASE WHEN fl_testdrive = 1 THEN month_dt ELSE 0 END) OVER(PARTITION BY id, flag_testdrive ORDER BY month_dt ROWS UNBOUNDED PRECEDING)
Here's an option for you:
DECLARE #Testdate TABLE(
id INT
,flag_testdrive INT
,month_dt INT
)
INSERT INTO #Testdate (
[id]
, [flag_testdrive]
, [month_dt]
)
VALUES(1,0,1)
,(1,0,2)
,(1,1,3)
,(1,1,4)
,(2,0,2)
,(2,0,3)
,(3,1,4)
,(3,1,5)
SELECT
*
,COALESCE((SELECT MIN([aa].[month_dt]) FROM #Testdate aa
WHERE aa.[id] = a.id
AND aa.[flag_testdrive] = 1), 0) AS month_testdrive
FROM #Testdate a
Return the minimum month_dt for a given id only if flag_testdrive=1, wrapped in coalesce to return 0 instead of NULL.

SQL Calculations based by field type and group by the type

Database includes FamID, TicketType and Amt
I want to get a calculation for total amount for each tickettype for each family and sort by family high to low based on total for all tickettypes.
Database values are:
FamID TicketType Amt
1 1 10
1 1 10
1 2 20
1 3 30
2 2 20
2 1 10
2 1 10
2 1 10
2 3 30
3 3 30
3 3 30
3 3 30
Would like results to be
Family Type 1 Type 2 Type 3 Total
3 0 0 90 90
2 30 20 30 80
1 20 20 30 70
Am I trying to do too much?
You never specified your RDBMS, but the following pivot query should work across most major ones with little modification:
SELECT t.`Type 1`, t.`Type 2`, t.`Type 3`,
(t.`Type 1` + t.`Type 2` + 2*t.`Type 3`) AS Total
FROM
(
SELECT FamID AS Family,
SUM(CASE WHEN TicketType = 1 THEN Amt ELSE 0 END) AS `Type 1`,
SUM(CASE WHEN TicketType = 2 THEN Amt ELSE 0 END) AS `Type 2`,
SUM(CASE WHEN TicketType = 3 THEN Amt ELSE 0 END) AS `Type 3`,
FROM Tickets
GROUP BY FamID
) t
ORDER BY t.Total DESC

SQLite query to GROUP BY nearby row

I have a SQLite .db file that contains the Thread table that looks like this:
ThreadID ClusterID
1 0
2 0
3 0
4 1
5 1
6 0
7 1
8 1
9 0
10 1
And I would like to GROUP BY the ClusterID by only with the nearby row. Output would be:
ThreadID ClusterID
1 0
4 1
6 0
7 1
9 0
10 1
Or ideally:
ThreadID ClusterID ClusterSwitch
1 0 NO
2 0 NO
3 0 NO
4 1 YES
5 1 NO
6 0 YES
7 1 YES
8 1 NO
9 0 YES
10 1 YES
The whole design its to detect when a cluster switched from 0 to 1 and from 1 to 0
Thanks for your help it is really appreciated :)
-Steve
Assuming your thread ids are really in order with no gaps, you can just use a self join:
select t.*,
(case when tprev.clusterid <> t.clusterid then 1 else 0 end) as ClusterSwitch
from threads t left join
threads tprev
on t.threadid = tprev.threadid + 1;
If you cannot be sure of no gaps, you can do this with a correlated subquery:
select t.*,
(case when t.clusterid <>
(select t2.clusterid
from threads t2
where t2.id < t.id
order by t2.id desc
limit 1
)
then 1 else 0 end) as ClusterSwitch
from threads t;
However, this query will not scale well, so performance could be an issue.

Update table records with accumulated result

Lets say I have a table Tbl (Represents simple timelogs for work made on different customers)
Five columns
Id: int
TimeUse: float
IdCustomer: int
Created: DateTime
TimeCalc: float
I have a number of records in this table, (TimeCalc is initialized to value = 0)
What I want my SQL to do is:
when TimeUse for all foregoing records on a specific customer accumulates to a value < 10 then the value in TimeCalc should be 0
when TimeUse for all foregoing records on a specific customer accumulates to a value >= 10 then the value in TimeCalc should be = TimeUse for the record...
I have messed around with Case routines with subqueries, but can't get it working.
BEFORE
Id TimeUse IdCustomer Created TimeCalc
1 2 1 14/09/09 0
2 5 2 14/09/10 0
3 2 1 14/09/11 0
4 5 2 14/09/12 0
5 4 1 14/09/13 0
6 2 2 14/09/14 0
7 4 1 14/09/15 0
8 1 1 14/09/16 0
9 3 2 14/09/17 0
10 2 1 14/09/18 0
11 4 2 14/09/19 0
AFTER
Id TimeUse IdCustomer Created TimeCalc
1 2 1 14/09/09 0
2 5 2 14/09/10 0
3 2 1 14/09/11 0
4 5 2 14/09/12 0
5 4 1 14/09/13 0
6 2 2 14/09/14 2
7 4 1 14/09/15 0
8 1 1 14/09/16 1
9 3 2 14/09/17 3
10 2 1 14/09/18 2
11 4 2 14/09/19 4
Can this be solved in an SQL update?
In SQL Server 2012+, you can do this with a cumulative sum:
select Id, TimeUse, IdCustomer, Created,
(case when sum(timeuse) over (partition by idcustomer order by id) < 10 then 0
else timeuse
end) as timecalc
from table t;
You can do the same thing in earlier versions using outer apply or a subquery.
If you want an update, just use a CTE:
with toupdate as (
select t.*,
(case when sum(timeuse) over (partition by idcustomer order by id) < 10 then 0
else timeuse
end) as new_timecalc
from table t
)
update toupdate
set timecalc = new_timecalc;
EDIT:
The following will work in any version of SQL Server:
with toupdate as (
select t.*,
(case when (select sum(t2.timeuse)
from table t2
where t2.idcustomer = t.idcustomer and
t2.id <= t.id
) < 10 then 0
else timeuse
end) as new_timecalc
from table t
)
update toupdate
set timecalc = new_timecalc;

Week based count

I have a requirement to retrieve the data in the below fashion
Weeks delay_count
0 6
1 0
2 3
3 4
4 0
5 1
6 0
7 0
8 0
9 0
10 2
11 0
12 0
13 0
14 0
15 3
Here weeks is the hard coded column from 0 to 15 and delay_count is the derived column. I have a column delay_weeks. Based on the values in this column I need to populate the values in the delay_count column (derived column)
delay_weeks column values are below.
blank
blank
blank
2
10
5
blank
3
2
10
2
3
3
3
0
0
15
22
29
Conditions:
When delay_weeks is blank or 0 then count in the delay_count column should be 1
When delay_weeks is 3 then in the delay_count column the count should be 1 under week 3
When delay_weeks is 10 then in the delay_count column the count should be 1 under week 10
When delay_weeks is greater than or equal to 15 then in the delay_count column the count should be 1 under week 15.
I wrote code like below
SELECT "Weeks", a."delay_count"
FROM (SELECT LEVEL AS "Weeks"
FROM DUAL
CONNECT BY LEVEL <= 15) m,
(SELECT VALUE, COUNT (VALUE) AS "delay_numbers"
FROM (SELECT CASE
WHEN attr11.VALUE >= 15
THEN '15'
ELSE attr11.VALUE
END
VALUE
FROM docs,
(SELECT object_id, VALUE, attribute_type_id
FROM ATTRIBUTES
WHERE attribute_type_id =
(SELECT attribute_type_id
FROM attribute_types
WHERE name_display_code =
'ATTRIBUTE_TYPE.DELAY IN WEEKS')) attr11
WHERE docs.obj_id = attr11.object_id(+)
GROUP BY VALUE) a
WHERE m."Weeks" = a.VALUE(+)
select
weeks,
nvl(cnt, 0) as delay_count
from
(select level-1 as weeks from dual connect by level < 17)
left join (
select
nvl(least(attr11.value, 15), 0) as weeks,
count(0) as cnt
from
DOCS
left join (
ATTRIBUTES attr11
join ATTRIBUTE_TYPES atr_tp using(attribute_type_id)
)
on atr_tp.name_display_code = 'ATTRIBUTE_TYPE.DELAY IN WEEKS'
and docs.obj_id = attr11.object_id
group by nvl(least(attr11.value, 15), 0)
) using(weeks)
order by 1
Reverse-engineering the relevant parts of the table definitions, I think this gives you what you want:
select t.weeks, count(delay) as delay_count
from (select level - 1 as weeks from dual connect by level <= 16) t
left join (
select case when a.value is null then 0
when to_number(a.value) > 15 then 15
else to_number(a.value) end as delay
from docs d
left join (
select a.object_id, a.value
from attributes a
join attribute_types at on at.attribute_type_id = a.attribute_type_id
where at.name_display_code = 'ATTRIBUTE_TYPE.DELAY IN WEEKS'
) a on a.object_id = d.obj_id
) delays on delays.delay = t.weeks
group by t.weeks
order by t.weeks;
With what I think is matching data I get:
WEEKS DELAY_COUNT
---------- -----------
0 6
1 0
2 3
3 4
4 0
5 1
6 0
7 0
8 0
9 0
10 2
11 0
12 0
13 0
14 0
15 3
But obviously since you haven't given the real table structures I'm guessing a bit on the relationships. Obligatory SQL Fiddle.