Count a specific value for many columns - tsql

I want to get an output that gives me the count of '0's of each column. For one column I get it by
select COUNT(*) from Q_Basis
where V1 = '0'
where the names of the relevant columns are v1, v2, ..., v60.
I have found out that I can do counts over all columns with
select count(*), *
but that does not help in my case since I still have to specify all columnnames in the where clause.
Any idea how I can acomplish this without hardcoding this in the where clause?

One way is to use conditional aggregation:
SELECT COUNT(CASE WHEN V1 = '0' THEN 1 END) As NumOfV1Zeros,
COUNT(CASE WHEN V2 = '0' THEN 1 END) As NumOfV2Zeros,
COUNT(CASE WHEN V3 = '0' THEN 1 END) As NumOfV3Zeros
FROM Q_Basis
WHERE '0' IN(V1, V2, V3) -- Don't count records that doesn't interest you...
You can't avoid specifying the column names individually, but if there are a lot of columns, you can dynamically create such a select statement using infromation_schema.columns.

Related

SQL query to put a number in a column and put an incremented number when there is a new text in a column

I have a query SELECT * from TABLE which gives the result as below table:
Expected column is as below:
I want to frame a new column like whenever we get the value as 0 then the number should be incremented by 1. I tried DENSE_RANK() , ROW_NUMBER() but couldn't get the exact result which mentioned. Is that possible in PostgreSQL.
Try This:
select name, value,
sum(case when value=0 then 1 else 0 end) over (order by "sno")
from (
select row_number() over() as "sno",* from example
) tab
DEMO
NOTE: Please note that there is no guaranteed that you will get same output always due no ordering field in your raw data.
So Better approach is to add some field in your view output by which it can be ordered and run the query like below:(assuming you have a ID field)
select
name,
value,
sum(case when value=0 then 1 else 0 end) over (order by id)
from example
DEMO

PostgreSQL crosstab() alternative with CASE and aggregates

I want to create a pivot table view showing month on month sum of bookings for every travel_mode.
Table bookings:
timestamp
, bookings
, provider_id
Table providers:
provider_id
, travel_mode
Pivot table function and crosstab functions are not to be used to do this. So I am trying to use JOIN and CASE. Following is the query:
SELECT b.month,
(CASE WHEN p.travel_mode=train then b.amount end)train,
(CASE WHEN p.travel_mode=bus then b.amount end)bus,
(CASE WHEN p.travel_mode=air then b.amount end)air
FROM
(SELECT to_char(date_,month) as month, travel_mode, sum(bookings) as amount
from bookings as b
join providers as p
on b.provider_id=p.provider_id
group by b.month, p.travel_mode)
group by b.month;
However I am getting an error which says:
subquery in FROM must have an alias LINE 6:
And when I add an alias it throws an error saying:
column p.travel_mode must appear in the GROUP BY clause or be used in an aggregate function
LINE 2:
The final result should be something like this
Month Air Bus Train
01 Amount(air) Amount(Bus) Amount(train)
I have a feeling it is a minor error somewhere but I am unable to figure it out at all.
P.S. I had to remove all quotations in the question as it was not allowing me to post this. But those are being taken care of in the actual query.
Multiple problems. The missing table alias is just one of them. This query should work:
SELECT month
, sum(CASE WHEN travel_mode = 'train' THEN amount END) AS train
, sum(CASE WHEN travel_mode = 'bus' THEN amount END) AS bus
, sum(CASE WHEN travel_mode = 'air' THEN amount END) AS air
FROM (
SELECT to_char(timestamp, 'MM') AS month, travel_mode, sum(bookings) AS amount
FROM bookings b
JOIN providers p USING (provider_id)
GROUP BY month, p.travel_mode
) sub
GROUP BY month;
Missing single quotes for string literals. (You seem to have removed those being under the wrong impression you couldn't post quotations.)
Missing table alias for the subquery - just like the 1st error message says.
In the outer query, table names (or aliases) of underlying tables in the subquery are not visible. Only the table alias of the subquery is. Since there is only one subquery, you don't need table-qualification at all there.
month is an output column name (not in the underlying table), so the table qualification b.month was wrong, too.
You seem to want 2-digit numbers for months. Use the template pattern 'MM' instead of 'month' with to_char().
The aggregation in the outer query does not work like you had it - just like your 2nd error message says. You have to wrap the outer CASE expression in a aggregate function. You might as well use min() or max() in this case, because there are never more than one rows after the subquery.
Still unclear where date_ is coming from? You mean timestamp? (which is not a good identifier).
But you don't need the subquery to begin with and can simplify to:
SELECT to_char(timestamp, 'MM') AS month
, sum(CASE WHEN p.travel_mode = 'train' THEN b.bookings END) AS train
, sum(CASE WHEN p.travel_mode = 'bus' THEN b.bookings END) AS bus
, sum(CASE WHEN p.travel_mode = 'air' THEN b.bookings END) AS air
FROM bookings b
JOIN providers p USING (provider_id)
GROUP BY 1;
For best performance you should still use crosstab(), though:
PostgreSQL Crosstab Query
You have to name the subquery as the error message says:
SELECT b.month,
(CASE WHEN p.travel_mode=train then b.amount end)train,
(CASE WHEN p.travel_mode=bus then b.amount end)bus,
(CASE WHEN p.travel_mode=air then b.amount end)air
FROM
(SELECT to_char(date_,month) as month, travel_mode, sum(bookings) as amount
from bookings as b
join providers as p
on b.provider_id=p.provider_id
group by b.month, p.travel_mode)
**as foo** group by b.month;
Remove the stars to make it work.

How to aggregate count of unique values in a column of pgsql table & display them in columns?

In Postgresql, one of my columns contain only contain one of 5 values. One of those 5 values is "Matured". I am trying to write a query that counts how many times "Matured" appears in that column for a specific cut of the whole table.
The following query produces the error: syntax error at or near "Matured".
The query:
select count(case when stagename is Matured end) from db_table
ALTERNATIVELY, I have the following result:
"Matured";694
"Credit Approved";3
"Delinquent";572
"Current";1356
"Canceled";16
using the query:
select distinct stagename,
sum(case when stagename is not null then 1 else 0 end)
from db_table
group by stagename
But I need that result in columns not rows. Like so:
|Matured |Credit Approved |Delinquent |Current |Canceled
stage_count |694 |3 |572 |1356 |16
Any ideas?
You have missed single quotes for Matured in your query as well as case when statement is not correct that is why it is returning syntax error. You can use below query to get required result.
You can also read about crosstab function of postgres.
select count(case when stagename = 'Matured' then 1 end),
count(case when stagename = 'Credit Approved' then 1 end),
count(case when stagename = 'Delinquent' then 1 end),
count(case when stagename = 'Current' then 1 end),
count(case when stagename = 'Canceled' then 1 end)
from db_table
Note: If your column contains leading and trailing space then use trim function with your column name and then compare.

find rows not following by the same values in 3 columns

I have a table named raw_data with the following data
as You can see id 1 and 2 share the same values in field desa, kecamatan and kabupaten, also id 3,4,5.
So basically I want to select all rows that is not followed by the same previous values. expected result would be:
I know it's easy to do this in any programming languages such as PHP, but I need this in postgresql. is this doable? Thanks in Advance.
Assuming higher id denotes latest row, if a row with same all three columns is present not together and you don't want to filter it out as it doesn't have same values as previous row (order by id or created_date), then you can make use of analytic lag() function:
select *
from (
select
t.*,
case
when desa = lag(desa) over (order by id)
and kecamatan = lag(kecamatan) over (order by id)
and kabupaten = lag(kabupaten) over (order by id)
then 0 else 1
end flag
from your_table t
) t where flag = 1;

Sql Server - Running Totals Based on Conditions

I have been banging my head trying to come up with the correct logic (SQL Server 2012) needed to achieve something I would imagine would be fairly routine but I have been unable to find any examples of this anywhere. Basically, I have 3 columns in a table: product, flag, value. It is possible for a product to be listed multiple times within the table but only once with a unique flag (i.e. product1 can have flag1 or flag2 with different/identical but there will never be 2 records with product1 and flag1 and different/identical values).
The flag represents a pre-defined value (1,2,3,4) and the intention behind this field is to be able to assign a unique mathematical equation based on the value of the flag. The end result would yield a single product, the unique flag, and a new cumulative total based on the mathematical equation output. For instance, let's say product1 was listed 4 times with flag values of flag1, flag2, flag3, flag4 (see below):
Product-----Flag-----Value
Product1----Flag1----1.00
Product1----Flag2----3.00
Product1----Flag3----5.00
Product1----Flag4----7.00
Product-----Flag-----Value
Product1----Flag1----1.00 (flag1 value)
Product1----Flag2----4.00 (flag1+flag2 value)
Product1----Flag3----6.00 (flag1+flag3 value)
Product1----Flag4----10.00 (flag2+flag4 value)
Flag1 is defined as add flag1 only. Flag2 is defined as add flag1 and flag2. Flag 3 is defined as add flag1 and flag 3. Flag 4 is defined as add flag2 and flag4. the new output would be product1 listed four times with flag values of flag1, flag2, flag3, flag4 but new values as flag1, flag1_flag2, flag1+flag3, flag2+flag4.
I have tried to apply the logic via a case statement but I can't figure out how to traverse all the products for each condition and I have tried to go with a running totals solution but I am not sure how to incorporate the flag condition into it so it only performs a running total for when those conditions are true. Any assistance and/or article to help get me going down the right path would be greatly appreciated.
While I'm not sure I fully understand your question I think this might be what you want. For this to work it assumes flag1 is always present when flags 1 through 3 are and that flag2 is present when flag4 is.
;with cte as (
select
product,
max(case when flag = 'Flag1' then Value end) as f1Value,
max(case when flag = 'Flag2' then Value end) as f2Value,
max(case when flag = 'Flag3' then Value end) as f3Value,
max(case when flag = 'Flag4' then Value end) as f4Value
from flags group by Product
)
select
flags.Product,
flags.Flag,
flags.Value as "Org. value",
case flag
when 'Flag1' then f1Value
when 'Flag2' then f1Value + f2Value
when 'Flag3' then f1Value + f3Value
when 'Flag4' then f2Value + f4Value
else flags.Value -- take the present value when flag is not Flag1-4
end as "New value"
from flags
inner join cte on flags.Product = cte.Product
Take a look at this Sample SQL Fiddle to see it in action.
You can join a table to itself, and pick the conditions appropriately:
SELECT p1.product,p1.Flag,p1.Value + COALESCE(p2.Value,0)
FROM
Products p1
left join
Products p2
on
p1.Product = p2.Product and
p2.Flag = CASE p1.Flag
--1 doesn't need a previous value
WHEN 2 THEN 1
WHEN 3 THEN 1
WHEN 4 THEN 2
END
I assumed and tried on Range values.
CREATE TABLE #tmp (Product VARCHAR(10), flag VARCHAR(10),value numeric(13,2))
GO
INSERT INTO #tmp
SELECT 'Product1' , 'Flag1',1
UNION
SELECT 'Product1' , 'Flag2',3
UNION
SELECT 'Product1' , 'Flag3',5
UNION
SELECT 'Product1' , 'Flag4',7
GO
;WITH cte
AS
(
SELECT row_number () OVER(
ORDER BY flag) 'row',*
FROM #tmp
)
SELECT *,value 'RT'
FROM cte
WHERE row = 1
UNION
SELECT * ,(
SELECT cte.value
FROM cte
WHERE row = 1
) + value 'RT'
FROM cte
WHERE row BETWEEN 2
AND 3
UNION
SELECT * ,(
SELECT cte.value
FROM cte
WHERE row =2
) + value 'RT'
FROM cte
WHERE row >3
GO
DROP TABLE #tmp