Postgresql: How to use "crosstab" in postgresql? - sql

Here i want to print the data in the following form.
Example:
callnumber1|callnumber2|CALL-IN|CALL-OUT|SMS-IN|SMS-OUT|FirstCallDate|LastCallDate
--------- +-------------+---------+----------+--------+---------+---------------+----
123456 | 654321 | 1 | 2 | 1 | 1 | 2014-02-12 | 2013-03-12
23456 | 54321 | 0 | 1 | 0 | 1 | 2014-02-12 | 2013-03-12
Table: Table1
create table table1
(
callnumber1 int,
callnumber2 int,
calltype varchar,
calldate date
);
Inserting some data
insert into table1 values(123456,654321,'CALL-IN','1-2-2014');
Crosstab query.
select * from crosstab($$select callnumber1,callnumber2,calltype,calldate,count(callnumber1||callnumber2) as totalcalls
from table1
where calltype in ('CALL-IN','CALL-OUT','SMS-IN','SMS-OUT')
group by callnumber1,callnumber2,calltype
order by callnumber1,callnumber2,calltype
$$,
$$ values('CALL-IN'),('CALL-OUT'),('SMS-IN'),('SMS-OUT')$$)
as table1(callnumber1 int,callnumber2 int,"CALL-IN" int,"CALL-OUT" int,"SMS-IN" int,"SMS-OUT" int,FirstCallDate date,LastCallDate date);

You don't need crosstab() for that. Conditional counts do the job:
SELECT callnumber1, callnumber2
, count(calltype = 'CALL-IN' OR NULL) AS call_in
, count(calltype = 'CALL-OUT' OR NULL) AS call_out
, count(calltype = 'SMS-IN' OR NULL) AS sms_in
, count(calltype = 'SMS-OUT' OR NULL) AS sms_out
, min(calldate) AS first_calldate
, max(calldate) AS last_calldate
, count(*) AS total_calls
FROM table1
WHERE calltype in ('CALL-IN','CALL-OUT','SMS-IN','SMS-OUT')
GROUP BY 1,2
ORDER BY 1,2
Use count(*) instead of count(callnumber1||callnumber2), assuming both columns are defined NOT NULL.
How does count(calltype = 'CALL-IN' OR NULL) work?
Compute percents from SUM() in the same SELECT sql query

Related

How to create a condition for this case?

Sample Table:
Id |Acc_Code|Description |Balance | Acclevel| Acctype| Exttype|
--- -------- ----------------- |-------- |-------- | -------| -------|
1 |SA |Sales | 0.00 | 1 | SA | |
2 |CS |Cost of Sales | 0.00 | 1 | CS | |
3 |5000/001|Revenue | 94.34 | 2 | SA | |
4 |5000/090|Sales(Local) | 62.83 | 2 | SA | |
5 |7000/000|Manufacturing Acc |-250.80 | 2 | CS | MA |
6 |7000/200|Manufacturing Acc | 178.00 | 2 | CS | |
This is a sample data of a temporary table which would be used to be inserted into another temporary table that would calculate the data for Profit and Loss Statement (For Manufacturing related Accounts only).
In this case, the acc_code for Manufacturing accounts start from 7000/000 and separated/partitioned for each following Exttype.
Eg: We start from the exttype of MA and based on its acclevel (could be 2 or more) until the next exttype.
The idea is we get the manufacturing accounts by SELECT FROM tmp_acc_list WHERE acc_code BETWEEN #start_acc_code (7000/000 in this case) AND #end_acc_code (the data before the next exttype)
I don't know what the exttype is, I'm still learning the tables.
How do we create the #end_acc_code part out from this sample table?
So here is a all in one script.
I created Your table for test:
create table #tmp_acc_list(
Id numeric,
Acc_Code nvarchar(100),
Acclevel numeric,
Acctype nvarchar(100),
Exttype nvarchar(100));
GO
insert into #tmp_acc_list(Id, Acc_Code, Acclevel, Acctype, Exttype)
select 1 , 'SA', 1,'SA', null union all
select 2 , 'CS', 1,'CS', null union all
select 3 , '5000/001', 2,'SA', null union all
select 4 , '5000/090', 2,'SA', null union all
select 5 , '7000/000', 2,'CS', 'MA' union all
select 6 , '7000/200', 2,'CS', null
;
Then comes the query:
with OrderedTable as -- to order the table is Id is not an order
(
select
t.*, ROW_NUMBER() over (
order by id asc --use any ordering You need here
)
as RowNum
from
#tmp_acc_list as t
),
MarkedTable as -- mark with common number
(
select
t.*,
Max(case when t.Exttype is null then null else t.RowNum end)
over (order by t.RowNum) as GroupRownum
from OrderedTable as t
),
GroupedTable as -- add group Exttype
(
select
t.Id, t.Acc_Code, t.Acclevel, t.Acctype, t.Exttype,
max(t.Exttype) over (partition by t.GroupRownum) as GroupExttype
from MarkedTable as t
)
select * from GroupedTable where GroupExttype = 'MA'
Is this what You need?
select *
from
(
select Id, Acc_Code
from tmp_acc_list
where Acc_Code = '7000/000'
) s
cross join tmp_acc_list a
cross apply
(
select top 1 x.Id, x.Acc_Code
from tmp_acc_list x
where x.Id >= a.Id
and x.AccLevel = a.AccLevel
and x.Acctype = a.Acctype
and x.Exttype = ''
order by Id desc
) e
where a.Id between s.Id and e.Id

SQL 1 row twice

I have SQL table what looks like:
+----------+-----------+
| ID | Direction |
+----------+-----------+
| 1 | left |
| 1 | null |
| 2 | left |
| 2 | null |
| 3 | null |
| 4 | left |
| 4 | null |
| 5 | null |
+----------+-----------+
I want to show each value only once:
If there will be ID 1 with Direction null and left, then show only ID 1 with direction left.
If there will be ID 1 only with null value, show it with null value.
Use a common table expression (cte):
with cte as
(
Your huge select...
)
select *
from cte t1
where t1.Direction = 'left'
or not exists (select * from cte t2
where t2.kanbanid = t1.kanbanid
and t2.Direction = 'left')
I.e. if your select has Direction 'left' for a kanbanid, return that row. Also return that row if same kanbanid has no Direction 'left' at all.
Why wont below query work:
select id,max(dir)
from #temp
group by id
below is test data:
create table #temp
(
id int,
dir char(10)
)
insert into #temp
select 1,'left'
union all
select 1,null
union all
select 2,null
union all
select 3,'right'
union all
select 3,null
union all
select 3,null
select id,max(dir)
from #temp
group by id
aggregate functions will ignore null,below is the output:
select distinct *,
row_number() over (partition by id order by ,Direction )as row1 into #any_table
from #your_table_name
select * from #any_table
where row1 =1

Counting Policy Ages with a Query in tsql

I have a table containing insurance policies (let's call it POLICIES) in one field, along with the policies off of which they were renewed in another field:
POLICY_ID | PRIOR_POLICY_ID
===========================
ABC |
ABD | ABC
AFP |
ANR | ABD
BRC | AFP
FKZ |
I would like to write a query to count the total number of prior policies for each policy, with the result looking like this:
POLICY_ID | NUM_PRIOR_POLICIES
==============================
ABC | 0
ABD | 1
AFP | 0
ANR | 2
BRC | 1
FKZ | 0
Any suggestions would be appreciated.
You need a recursive CTE for this:
with cte as (
select p.policy_id, 0 as num_priors
from policies p
where prior_policy_id is null
union all
select p.policy_id, 1 + cte.num_priors
from cte join
policies p
on p.prior_policy_id = cte.policy_id
)
select *
from cte;
Here is a SQL Fiddle showing it working.
DECLARE #data TABLE ( POLICY_ID char(3), PRIOR_POLICY_ID char(3) );
INSERT #data VALUES
('ABC',NULL ),('ABD','ABC'),('AFP',NULL ),
('ANR','ABD'),('BRC','AFP'),('FKZ',NULL );
WITH cte AS (
SELECT POLICY_ID, 0 AS NUM_PRIOR_POLICIES
FROM #data
WHERE PRIOR_POLICY_ID IS NULL
UNION ALL
SELECT d.POLICY_ID, NUM_PRIOR_POLICIES + 1
FROM cte c
INNER JOIN #data d
ON (c.POLICY_ID = d.PRIOR_POLICY_ID)
)
SELECT POLICY_ID, NUM_PRIOR_POLICIES
FROM cte
ORDER BY POLICY_ID

SQL Order By and "Not-So-Much Group"

Lets say I have a table:
--------------------------------------
| ID | DATE | GROUP | RESULT |
--------------------------------------
| 1 | 01/06 | Group1 | 12345 |
| 2 | 01/05 | Group2 | 54321 |
| 3 | 01/04 | Group1 | 11111 |
--------------------------------------
I want to order the result by the most recent date at the top but group the "group" column together, but still have distinct entries. The result that I want would be:
1 | 01/06 | Group1 | 12345
3 | 01/04 | Group1 | 11111
2 | 01/05 | Group2 | 54321
What would be a query to get that result?
thank you!
EDIT:
I'm using MSSQL. I'll look into translating the oracle query into MS SQL and report my results.
EDIT
SQL Server 2000, so OVER/PARTITION is not supported =[
Thank you!
You should specify what RDBMS you are using. This answer is for Oracle, may not work in other systems.
SELECT * FROM table
ORDER BY MAX(date) OVER (PARTITION BY group) DESC, group, date DESC
declare #table table (
ID int not null,
[DATE] smalldatetime not null,
[GROUP] varchar(10) not null,
[RESULT] varchar(10) not null
)
insert #table values (1, '2009-01-06', 'Group1', '12345')
insert #table values (2, '2009-01-05', 'Group2', '12345')
insert #table values (3, '2009-01-04', 'Group1', '12345')
select t.*
from #table t
inner join (
select
max([date]) as [order-date],
[GROUP]
from #table orderer
group by
[GROUP]
) x
on t.[GROUP] = x.[GROUP]
order by
x.[order-date] desc,
t.[GROUP],
t.[DATE] desc
use an order by clause with two params:
...order by group, date desc
this assumes that your date column does hold dates and not varchars
SELECT table2.myID,
table2.mydate,
table2.mygroup,
table2.myresult
FROM (SELECT DISTINCT mygroup FROM testtable as table1) as grouptable
JOIN testtable as table2
ON grouptable.mygroup = table2.mygroup
ORDER BY grouptable.mygroup,table2.mydate
SORRY, could NOT bring myself to use columns that were reserved names, rename the columns to make it work :)
this is MUCH simpler than the accepted answer btw.

What is best way to write query with multiple aggregate columns and multiple where clause

I have a table tbl_LEDGER
+-----------+--------+-----+
| AccountId | GlCode | Amt |
+-----------+--------+-----+
| LAS00001 | INTRAC | 100 |
| LAS00002 | INTRAC | 150 |
| LAS00001 | INTLAS | 200 |
+-----------+--------+-----+
Desired Result :
+-----------+------------+-----------+
| AccountId | intractamt | intlasAmt |
+-----------+------------+-----------+
| LAS00001 | 100 | 200 |
| LAS00002 | 150 | 0 |
+-----------+------------+-----------+
Here is my working query:
select accountid,sum(amt) intracamt, (select SUM(amt) from tbl_LEDGER
where GLCode='intlas' and AccountID=intrac.AccountID ) intlasamt
from tbl_LEDGER intrac
where GLCode='intrac' group by AccountID order by AccountID
Another Working query :
select a.accountId,a.amt as 'RACAMT',b.amt as 'LACAMT' from
(
select accountid ,glcode, SUM(amt) as amt from nbfcledger where GLCode='intrac' group by GLCode,AccountID
) a
inner join
(
select accountid ,glcode, SUM(amt) as amt from nbfcledger where GLCode='intlas' group by GLCode,AccountID
)b
on a.AccountID = b.AccountID order by AccountID
What are the other ways in which I can achieve the same result? Which one is best and why? I was hoping I could do this without PIVOT.
select AccountId,
Sum(case when GlCode = 'INTRAC' then amt else 0 end ) as intractamt,
Sum(case when GlCode = 'INTLAS' then amt else 0 end ) as intlasAmt
from tbl_LEDGER
group by AccountId
Another way to do the same is as given above.
This is my approach:
DECLARE #tbl_LEDGER TABLE(AccountId VARCHAR(100),GlCode VARCHAR(100),Amt DECIMAL(8,4));
INSERT INTO #tbl_LEDGER VALUES
('LAS00001','INTRAC',100)
,('LAS00002','INTRAC',150)
,('LAS00001','INTLAS',200);
WITH IntracValues AS
(
SELECT *
FROM #tbl_LEDGER
WHERE GlCode='INTRAC'
)
,IntlasValues AS
(
SELECT *
FROM #tbl_LEDGER
WHERE GlCode='INTLAS'
)
SELECT IntracValues.AccountId
,ISNULL(IntracValues.Amt,0) AS intractAmt
,ISNULL(IntlasValues.Amt,0) AS intlasAmt
FROM IntracValues
FULL OUTER JOIN IntlasValues ON IntracValues.AccountId=IntlasValues.AccountId
Could you check this:
WITH DataSource ([AccountId], [Type], [Amount]) AS
(
SELECT [AccountId]
,'intractamt'
,Sum(amt)
FROM tbl_LEDGER
WHERE [GlCode] = 'INTRAC'
GROUP BY AccountId
UNION ALL
SELECT AccountId
,'intlasAmt'
,Sum(amt)
FROM tbl_LEDGER
WHERE [GlCode] = 'INTLAS'
GROUP BY [AccountId]
)
SELECT [AccountId]
,[intractamt]
,[intlasAmt]
FROM DataSource
PIVOT
(
MAX([Amount]) FOR [Type] IN ([intractamt], [intlasAmt])
) PVT
If you are not testing on production execute the queries with:
DBCC DROPCLEANBUFFERS;
SET STATISTICS IO ON;
and use the following site to compare the statistics from the message tab.