I have OpenStreetMap data loaded to a PostgreSQL table. A hstore type column contains all of the tags. I would like to make a comparison chart to see how many records has name, name:en, name:bg tags for example. The result I would like to see is something like this:
I can achieve this manually using this query:
SELECT 1 AS id, '+' AS name, NULL AS "name:en", NULL AS "name:bg", count(*) FROM public.ways WHERE exist(tags,'name') UNION
SELECT 2 AS id, NULL AS name, '+' AS "name:en", NULL AS "name:bg", count(*) FROM public.ways WHERE exist(tags,'name:en') UNION
SELECT 3 AS id, NULL AS name, NULL AS "name:en", '+' AS "name:bg", count(*) FROM public.ways WHERE exist(tags,'name:bg') UNION
SELECT 4 AS id, '+' AS name, '+' AS "name:en", NULL AS "name:bg", count(*) FROM public.ways WHERE exist(tags,'name') AND exist(tags,'name:en') UNION
SELECT 5 AS id, '+' AS name, NULL AS "name:en", '+' AS "name:bg", count(*) FROM public.ways WHERE exist(tags,'name') AND exist(tags,'name:bg') UNION
SELECT 6 AS id, '+' AS name, '-' AS "name:en", NULL AS "name:bg", count(*) FROM public.ways WHERE exist(tags,'name') AND NOT exist(tags,'name:en') UNION
SELECT 7 AS id, '-' AS name, '+' AS "name:en", NULL AS "name:bg", count(*) FROM public.ways WHERE NOT exist(tags,'name') AND exist(tags,'name:en')
ORDER BY id
I consider this unnecessarily long and overcomplicated, plus I have to do it manually. I know there are some possibilities using the crosstab function, but I couldn't get it working. Based on the answer to this question I was able to create something like this:
SELECT * FROM crosstab(
'SELECT tags::text~''"name"=>".*"'' as a, tags::text~''"name:en"=>".*"'' as b, tags::text~''"name_int"=>".*"'' as c FROM public.ways')
AS ct (name boolean,"name:en" boolean, "name:bg" boolean)
GROUP BY name,"name:en","name:bg"
My problem is that I cannot seem to add a count column to this, and that it does not contain options where only one of the three condition is taken into account.
Any idea how could I solve this problem, or any direction where should I start?
Example data lines:
1 "name"=>"dm"
2 "name"=>"Ешекчи дере", "name:en"=>"Khatak Dere River"
3 "name:en"=>"Sushitsa"
4 "name"=>"Слънчева", "name:bg"=>"Слънчева", "name:en"=>"Slantcheva"
Hello look if its works for you , it is possible to generate a join from a emulated table from a select to group the values :
SELECT row_number() OVER() AS id ,COUNT(*) AS count , COALESCE(a.tags , '')||COALESCE(b.tags,'')||COALESCE(c.tags ,'') AS tagcombination,
CASE WHEN COALESCE(a.tags , '')||COALESCE(b.tags,'')||COALESCE(c.tags ,'')="name:en" THEN '+'
WHEN COALESCE(a.tags , '')||COALESCE(b.tags,'')||COALESCE(c.tags ,'') = 'name:en' THEN '+' END AS name
FROM public.ways AS a
LEFT JOIN (SELECT DISTINCT tags FROM public.ways WHERE tags = 'name' ) AS b ON a.tags = b.tags
LEFT JOIN (SELECT DISTINCT tags FROM public.ways WHERE tags IN('name:en', 'name:bg' ) ) AS c ON a.tags = c.tags
JOIN (SELECT generate_series )
GROUP BY tagcombination
--WHERE a.tags IS NOT NULL
--ORDER BY name
The name column could be translated into numbers from the tagscombination and even be ordered later if it fits better your relatory.
Need to do the test and use a predicate to filter if there is more values possibilities than you want to count in the table also.
I have a sample data set as below:
TOPIC STATUS
Physics C
Maths I
Chemistry C
Chemistry I
Chemistry I
Maths C
Maths C
Physics I
Physics C
Maths C
How do I get percent of TOPIC which is completed against the total attempts like below?
Formulae for percent is 100*(Total No. of C/Total No. of Occurrence)
TOPIC PERCENT
Physics 66.66
Maths 75
Chemistry 33.33
Usually I write sub-queries to get this if in T-SQL(I'm already familiar with).
However, I'm new to postgreSQL and before I write the same sub-queries just wanted to check if there is an efficient way to get this in postgreSQL.
Update as requested
I get expected result with the below T-SQL code but looking for an efficient way using psql
IF OBJECT_ID('tempDB..##SOO',N'U') IS NOT NULL
DROP TABLE ##SOO
GO
SELECT * INTO ##SOO FROM(
SELECT 'PHYSICS' [TOPIC],'C' [STATUS]
UNION ALL
SELECT 'PHYSICS' [TOPIC],'C' [STATUS]
UNION ALL
SELECT 'PHYSICS' [TOPIC],'I' [STATUS]
UNION ALL
SELECT 'MATHS' [TOPIC],'C' [STATUS]
UNION ALL
SELECT 'MATHS' [TOPIC],'C' [STATUS]
UNION ALL
SELECT 'MATHS' [TOPIC],'I' [STATUS]
UNION ALL
SELECT 'MATHS' [TOPIC],'C' [STATUS]
UNION ALL
SELECT 'CHEMISTRY' [TOPIC],'I' [STATUS]
UNION ALL
SELECT 'CHEMISTRY' [TOPIC],'I' [STATUS]
UNION ALL
SELECT 'CHEMISTRY' [TOPIC],'C' [STATUS]
)A
GO
SELECT S.TOPIC, 100*S.PASS/S.KNT [PERCENTO] FROM(
SELECT A.TOPIC,A.KNT,B.PASS FROM(
SELECT TOPIC, COUNT(1) [KNT] FROM ##SOO
GROUP BY TOPIC)A
JOIN
(SELECT TOPIC, COUNT(1) [PASS] FROM ##SOO
WHERE [STATUS] ='C'
GROUP BY TOPIC)B ON A.TOPIC=B.TOPIC
)S
That query should work just fine in Postgres if you change those identifiers to comply with standard SQL.
However it can indeed be simplified in Postgres (and SQL Server):
select topic,
(count(*) filter (where status = 'C'))::numeric / count(*) as pct
from soo
group by topic;
Note that the above would also work in SQL Server if you replace the filter() clause with a case expression:
select topic,
(count(case when status = 'C' then 1 end))::numeric / count(*) as pct
from soo
group by topic;
Online example: http://rextester.com/HGTZJ63653
I have a table ProductNumberDuplicates_backups, which has two columns named ProductID and ProductNumber. There are some duplicate ProductNumbers. How can I count the distinct number of products, then print out the outcome like "() products was backup." ? Because this is inside a stored procedure, I have to use a variable #numrecord as the distinct number of rows. I put my codes like this:
set #numrecord= select distinct ProductNumber
from ProductNumberDuplicates_backups where COUNT(*) > 1
group by ProductID
having Count(ProductNumber)>1
Print cast(#numrecord as varchar)+' product(s) were backed up.'
obviously the error was after the = sign as the select can not follow it. I've search for similar cases but they are just select statements. Please help. Many thanks!
Try
select #numrecord= count(distinct ProductNumber)
from ProductNumberDuplicates_backups
Print cast(#numrecord as varchar)+' product(s) were backed up.'
begin tran
create table ProductNumberDuplicates_backups (
ProductNumber int
)
insert ProductNumberDuplicates_backups(ProductNumber)
select 1
union all
select 2
union all
select 1
union all
select 3
union all
select 2
select * from ProductNumberDuplicates_backups
declare #numRecord int
select #numRecord = count(ProductNumber) from
(select ProductNumber, ROW_NUMBER()
over (partition by ProductNumber order by ProductNumber) RowNumber
from ProductNumberDuplicates_backups) p
where p.RowNumber > 1
print cast(#numRecord as varchar) + ' product(s) were backed up.'
rollback
I am using SSMS 2008 and trying to use a HAVING statement. This should be a real simple query. However, I am only getting one record returned event though there are numerous duplicates.
Am I doing something wrong with the HAVING statement here? Or is there some other function that I could use instead?
select
address_desc,
people_id
from
dbo.address_view
where people_id is not NULL
group by people_id , address_desc
having count(*) > 1
sample data from address_view:
people_id address_desc
---------- ------------
Murfreesboro, TN 37130 F15D1135-9947-4F66-B778-00E43EC44B9E
11 Mohawk Rd., Burlington, MA 01803 C561918F-C2E9-4507-BD7C-00FB688D2D6E
Unknown, UN 00000 C561918F-C2E9-4507-BD7C-00FB688D2D6E
Jacksonville, NC 28546 FC7C78CD-8AEA-4C8E-B93D-010BF8E4176D
Memphis, TN 38133 8ED8C601-5D35-4EB7-9217-012905D6E9F1
44 Maverick St., Fitchburg, MA 8ED8C601-5D35-4EB7-9217-012905D6E9F1
The GROUP BY is going to lump your duplicates together into a single row.
I think instead, you want to find all people_id values with duplicate address_desc:
SELECT a.address_desc, a.people_id
FROM dbo.address_view a
INNER JOIN (SELECT address_desc
FROM dbo.address_view
GROUP BY address_desc
HAVING COUNT(*) > 1) t
ON a.address_desc = t.address_desc
using row_number and partition you can find the duplicate occurrences where row_num>1
select address_desc,
people_id,
row_num
from
(
select
address_desc,
people_id,
row_number() over (partition by address_desc order by address_desc) row_num
from
dbo.address_view
where people_id is not NULL
) x
where row_num>1
Just a brief of business scenario is table has been created for a good receipt. So here we have good expected line with PurchaseOrder(PO) in first few line. And then we receive each expected line physically and that time these quantity may be different, due to business case like quantity may damage and short quantity like that. So we maintain a status for that eg: OK, Damage, also we have to calculate short quantity based on total of expected quantity of each item and total of received line.
if object_id('DEV..Temp','U') is not null
drop table Temp
CREATE TABLE Temp
(
ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
Item VARCHAR(32),
PO VARCHAR(32) NULL,
ExpectedQty INT NULL,
ReceivedQty INT NULL,
[STATUS] VARCHAR(32) NULL,
BoxName VARCHAR(32) NULL
)
Please see first few line with PO data will be the expected lines,
and then rest line will be received line
INSERT INTO TEMP (Item,PO,ExpectedQty,ReceivedQty,[STATUS],BoxName)
SELECT 'ITEM01','PO-01','30',NULL,NULL,NULL UNION ALL
SELECT 'ITEM01','PO-02','20',NULL,NULL,NULL UNION ALL
SELECT 'ITEM02','PO-01','40',NULL,NULL,NULL UNION ALL
SELECT 'ITEM03','PO-01','50',NULL,NULL,NULL UNION ALL
SELECT 'ITEM03','PO-02','30',NULL,NULL,NULL UNION ALL
SELECT 'ITEM03','PO-03','20',NULL,NULL,NULL UNION ALL
SELECT 'ITEM04','PO-01','30',NULL,NULL,NULL UNION ALL
SELECT 'ITEM01',NULL,NULL,'20','OK','box01' UNION ALL
SELECT 'ITEM01',NULL,NULL,'25','OK','box02' UNION ALL
SELECT 'ITEM01',NULL,NULL,'5','DAMAGE','box03' UNION ALL
SELECT 'ITEM02',NULL,NULL,'38','OK','box04' UNION ALL
SELECT 'ITEM02',NULL,NULL,'2','DAMAGE','box05' UNION ALL
SELECT 'ITEM03',NULL,NULL,'30','OK','box06' UNION ALL
SELECT 'ITEM03',NULL,NULL,'30','OK','box07' UNION ALL
SELECT 'ITEM03',NULL,NULL,'30','OK','box08' UNION ALL
SELECT 'ITEM03',NULL,NULL,'10','DAMAGE','box09' UNION ALL
SELECT 'ITEM04',NULL,NULL,'25','OK','box10'
Below Table is my expected result based on above data.
I need to show those data following way.
So I appreciate if you can give me an appropriate query for it.
Note: first row is blank and it is actually my table header. :)
SELECT '' as 'ITEM', '' as 'PO#', '' as 'ExpectedQty',
'' as 'ReceivedQty','' as 'DamageQty' ,'' as 'ShortQty' UNION ALL
SELECT 'ITEM01','PO-01','30','30','0' ,'0' UNION ALL
SELECT 'ITEM01','PO-02','20','15','5' ,'0' UNION ALL
SELECT 'ITEM02','PO-01','40','38','2' ,'0' UNION ALL
SELECT 'ITEM03','PO-01','50','50','0' ,'0' UNION ALL
SELECT 'ITEM03','PO-02','30','30','0' ,'0' UNION ALL
SELECT 'ITEM03','PO-03','20','10','10','0' UNION ALL
SELECT 'ITEM04','PO-01','30','25','0' ,'5'
Note : we don't received more than expected.
solution should be based on SQL 2000
You should reconsider how you store this data. Separate Expected and Received+Damaged in different tables (you have many unused (null) cells). This way any query should become more readable.
I think what you try to do can be achieved more easily with a stored procedure.
Anyway, try this query:
SELECT Item, PO, ExpectedQty,
CASE WHEN [rec-consumed] > 0 THEN ExpectedQty
ELSE CASE WHEN [rec-consumed] + ExpectedQty > 0
THEN [rec-consumed] + ExpectedQty
ELSE 0
END
END ReceivedQty,
CASE WHEN [rec-consumed] < 0
THEN CASE WHEN DamageQty >= -1*[rec-consumed]
THEN -1*[rec-consumed]
ELSE DamageQty
END
ELSE 0
END DamageQty,
CASE WHEN [rec_damage-consumed] < 0
THEN DamageQty - [rec-consumed]
ELSE 0
END ShortQty
FROM (
select t1.Item,
t1.PO,
t1.ExpectedQty,
st.sum_ReceivedQty_OK
- (sum(COALESCE(t2.ExpectedQty,0))
+t1.ExpectedQty)
[rec-consumed],
st.sum_ReceivedQty_OK + st.sum_ReceivedQty_DAMAGE
- (sum(COALESCE(t2.ExpectedQty,0))
+t1.ExpectedQty)
[rec_damage-consumed],
st.sum_ReceivedQty_DAMAGE DamageQty
from #tt t1
left join #tt t2 on t1.Item = t2.Item
and t1.PO > t2.PO
and t2.PO is not null
join (select Item
, sum(CASE WHEN status = 'OK' THEN ReceivedQty ELSE 0 END)
sum_ReceivedQty_OK
, sum(CASE WHEN status != 'OK' THEN ReceivedQty ELSE 0 END)
sum_ReceivedQty_DAMAGE
from #tt where PO is null
group by Item) st on t1.Item = st.Item
where t1.PO is not null
group by t1.Item, t1.PO, t1.ExpectedQty,
st.sum_ReceivedQty_OK,
st.sum_ReceivedQty_DAMAGE
) a
order by Item, PO