Postgres SQL query pivot without crosstab - postgresql

Select case_file_profile.case_file_id,
individual_profile.individual_name,
document_answer_details.document_template_question_id,
document.document_template_stage_id,
document.document_date,
individual_profile.individual_date_of_birth,
document.document_id,
document.document_template_id,
case_file_individuals.case_file_individual_relationship,
individual.individual_id,
case_file_date_3, case_file_close_date,
individual_profile.site_id,
document_answer_number.answer_number
FROM document
left join document_body on
(document.document_id = document_body.document_id)
left join document_answer_details on
(document_body.document_body_id = document_answer_details.document_body_id)
left join individual on
(document_body.entity_id = individual.entity_id)
left join individual_profile on
(individual.individual_id = individual_profile.individual_id)
left join case_file_individuals on
(individual_profile.individual_id = case_file_individuals.individual_id)
left join case_file_profile on
(case_file_individuals.case_file_id = case_file_profile.case_file_id)
full join document_answer_number on
(document_answer_details.document_answer_details_id =
document_answer_number.document_answer_details_id)
Where (conditions)
)
)
What it looks like
client id
Question ID
Answer num
53558
expected 100271
7
---------------------
------------------
------------
53558
completed 100272
6
---------------------
------------------
------------
What I'd like it to look like
client id
expected visits
completed visits
% of visits completed
53558
7
6
85%
---------------
----------------
--------------------
------------------------
The results are two rows of data per person. I need the results in separate columns with an additional column with a calculated value from the two numbers I need side by side. The numbers are connected to the particular person via case_file_id from the table case_file_individual. I'm not able to use crosstab because I don't have write permissions. I've also seen and tried CASE statements and haven't been able to get it to work.

Related

How to use 'Distinct' for just one column?

I have a query checking the visits from some "locations" table I have. If the user signed up with a referral of "emp" or "oth", their first visit shouldn't count but the second visit and forward should count.
I'm trying to get a count of those "first visits" per location. Whenever they do a visit, I get a record on which location it was.
The problem is that my query is counting correctly, but some users have visits on different locations. So instead of just counting one visit for that location (the first one), is adding one per location where a user has done a visit.
This is my query
SELECT COUNT(DISTINCT CASE WHEN customer.ref IN ('emp', 'oth') THEN customer.id END) as visit_count, locations.name as location FROM locations
LEFT JOIN visits ON locations.location_name = visits.location_visit_name
LEFT JOIN customer ON customer.id = visits.customer_id
WHERE locations.active = true
GROUP BY locations.location_name, locations.id;
The results I'm getting are
visit_count | locations
-------------------------
7 | Loc 1
3 | Loc 2
1 | Loc 3
How it should be:
visit_count | locations
-------------------------
6 | Loc 1
2 | Loc 2
1 | Loc 3
Because 2 of these people have visits on both locations, so its counting one for each location. I think the DISTINCT is also doing it for the locations, when it should be only on the counting for the customer.id
Is there a way I can add something to my query to just grab the location for the first visit, without caring they have done other visits on other locations?
If I followed you correctly, you want to count only the first visit of each customer, spread by location.
One solution would be to use a correlated subquery in the on clause of the relevant join to filter on first customer visits. Assuming that column visit(visit_date) stores the date of each visit, you could do:
select
count(c.customer_id) visit_count,
l.name as location
from locations l
left join visits v
on l.location_name = v.location_visit_name
and v.visit_date = (
select min(v1.visit_date)
from visit v1
where v1.customer_id = v.customer_id
)
left join customer c
on c.id = v.customer_id
and c.ref in ('emp', 'oth')
where l.active = true
group by l.location_name, l.id;
Side notes:
properly fitering on the first visit per customer avoids the need for distinct in the count() aggregate function
table aliases make the query more concise and easier to understand; I recommend to use them in all queries
the filter on customer(ref) is better placed in the where clause than as a conditional count criteria
Try moving the when condition in where clause
SELECT COUNT( distinct customer.id) as visit_count
, locations.name as location
FROM locations
LEFT JOIN visits ON locations.location_name = visits.location_visit_name
LEFT JOIN customer ON customer.id = visits.customer_id
WHERE locations.active = true
AND customer.ref IN ('emp', 'oth')
GROUP BY locations.location_name;c

include in result set columns the 5 returned columns in a User Defined Table Function

I have a UDTF that will always return 1 row of 6 columns
The UDTF has one parameter
I want the 5 columns included in the result set of a query. The table I'm querying has a column that I want to use as the parameter for each row
I have not been able to figure out the correct syntax.
Any suggestions?
The UDTF
create function xxxx.UF_yyyyyy(USERID CHAR(10))
returns table (
p2User char(10),
STATUS CHAR(3),
USED DEC(7, 0),
CREATED DEC(7, 0),
SIGNON DEC(7, 0),
EXCLUDE DEC(7, 0))
language RPGLE
NOT DETERMINISTIC
NO SQL
DISALLOW PARALLEL
NOT FENCED
EXTERNAL NAME 'xxxx/UF_yyyyyy'
PARAMETER STYLE DB2SQL
example
select * from table(xxxx/UF_yyyyyy(CHAR('CMFIRST '))) a
result
P2USER STATUS USED CREATED SIGNON EXCLUDE
---------- ------ ------- ------- ------- -------
CMFIRST ACT 1170926 1150826 1170926 0
Here is an example of a select I tried
SELECT T1.AQABVN, T1.AQA8TX,
(SELECT COUNT(*) FROM fffff T4 WHERE T4.BDABVN = T1.AQABVN) AS SACCMS,
t2.p2User, t2.used
FROM
zzzzz T1
full join table(xxxx.UF_yyyyyy(T1.AQABVN)) t2 on T1.AQABVN = t2.p2User
Result
[SQL0205] Column P2USER not in table T2 in *N.
Got it working
SELECT T1.AQABVN, T1.AQA8TX,
(SELECT COUNT(*) FROM fffff T4 WHERE T4.BDABVN = T1.AQABVN) AS SACCMS,
t2.status, t2.used, t2.created, t2.signon, t2.exclude
FROM
zzzzz T1
join
table(SMLFQA.UF_XAJKUPR(T1.AQABVN)) t2 on T1.AQABVN = t2.p2User

Common Table Expression return Nothing but "command executed"

i have a user table, a pro table and a pro_users table to join : user -- many to many --> pro
i want to get the number of users, the number of pro joined to user AND the sum of user+member (this seams not relevant but it's just for testing)
i have this query :
WITH sums AS(
SELECT COUNT(user.user_id) as usercount, COUNT(pro.pro_id) as procount
FROM user
LEFT JOIN pro_users ON user.user_id = pro_users.user_id
LEFT JOIN pro ON pro_users.pro_id = pro.pro_id
)
SELECT usercount, procount, usercount+procount as total FROM sums
the inner select work as expected when queried alone. I've tried to only get members from the outter select and absolutely no result show up and i get
message:Command Executed
in PostgreSql Studio.
desired output:
| usercount | procount | total |
--------------------------------
| 1000 | 500 | 1500 |
What am i mising?
Thanks

counting in sql in subquery in the table

DNO DNAME
----- -----------
1 Research
2 Finance
EN ENAME CITY SALARY DNO JOIN_DATE
-- ---------- ---------- ---------- ---------- ---------
E1 Ashim Kolkata 10000 1 01-JUN-02
E2 Kamal Mumbai 18000 2 02-JAN-02
E3 Tamal Chennai 7000 1 07-FEB-04
E4 Asha Kolkata 8000 2 01-MAR-07
E5 Timir Delhi 7000 1 11-JUN-05
//find all departments that have more than 3 employees.
My try
select deptt.dname
from deptt,empl
where deptt.dno=empl.dno and (select count(empl.dno) from empl group by empl.dno)>3;
here is the solution
select deptt.dname
from deptt,empl
where deptt.dno=empl.dno
group by deptt.dname having count(1)>3;
select
*
from departments d
inner join (
select dno from employees group by dno having count(*) > 3
) e on d.dno = e.dno
There are many approaches to this problem but almost all will use GROUP BY and the HAVING clause. That clause allows you to filter results of aggregate functions. Here it is used to choose only those records where the count is greater than 3.
In the query structure used above the group by is handled on the employee table only, then the result (which is known as a derived table) is joined by an INNER JOIN to the departments table. This inner join only allows matching records so this has the effect of filtering the departments table to only those which have a count() of greater than 3.
An advantage of this query structure is fewer records are joined, and also that all columns of the departments table are available for reporting. Disadvantage of this structure is the the count() of employees per department isn't visible.

SSRS 2005 column chart: show series label missing when data count is zero

I have a pretty simple chart with a likely common issue. I've searched for several hours on the interweb but only get so far in finding a similar situation.
the basics of what I'm pulling contains a created_by, person_id and risk score
the risk score can be:
1 VERY LOW
2 LOW
3 MODERATE STABLE
4 MODERATE AT RISK
5 HIGH
6 VERY HIGH
I want to get a headcount of persons at each risk score and display a risk count even if there is a count of 0 for that risk score but SSRS 2005 likes to suppress zero counts.
I've tried this in the point labels
=IIF(IsNothing(count(Fields!person_id.value)),0,count(Fields!person_id.value))
Ex: I'm missing values for "1 LOW" as the creator does not have any "1 LOW" they've assigned risk scores for.
*here's a screenshot of what I get but I'd like to have a column even for a count when it still doesn't exist in the returned results.
#Nathan
Example scenario:
select professor.name, grades.score, student.person_id
from student
inner join grades on student.person_id = grades.person_id
inner join professor on student.professor_id = professor.professor_id
where
student.professor_id = #professor
Not all students are necessarily in the grades table.
I have a =Count(Fields!person_id.Value) for my data points & series is grouped on =Fields!score.Value
If there were a bunch of A,B,D grades but no C & F's how would I show labels for potentially non-existent counts
In your example, the problem is that no results are returned for grades that are not linked to any students. To solve this ideally there would be a table in your source system which listed all the possible values of "score" (e.g. A - F) and you would join this into your query such that at least one row was returned for each possible value.
If such a table doesn't exist and the possible score values are known and static, then you could manually create a list of them in your query. In the example below I create a subquery that returns a combination of all professors and all possible scores (A - F) and then LEFT join this to the grades and students tables (left join means that the professor/score rows will be returned even if no students have those scores in the "grades" table).
SELECT
professor.name
, professorgrades.score
, student.person_id
FROM
(
SELECT professor_id, score
FROM professor
CROSS JOIN
(
SELECT 'A' AS score
UNION
SELECT 'B'
UNION
SELECT 'C'
UNION
SELECT 'D'
UNION
SELECT 'E'
UNION
SELECT 'F'
) availablegrades
) professorgrades
INNER JOIN professor ON professorgrades.professor_id = professor.professor_id
LEFT JOIN grades ON professorgrades.score = grades.score
LEFT JOIN student ON grades.person_id = student.person_id AND
professorgrades.professor_id = student.professor_id
WHERE professorgrades.professor_id = 1
See a live example of how this works here: SQLFIDDLE
SELECT RS.RiskScoreId, RS.Description, SUM(DT.RiskCount) AS RiskCount
FROM (
SELECT RiskScoreId, 1 AS RiskCount
FROM People
UNION ALL
SELECT RiskScoreId, 0 AS RiskCount
FROM RiskScores
) DT
INNER JOIN RiskScores RS ON RS.RiskScoreId = DT.RiskScoreId
GROUP BY RS.RiskScoreId, RS.Description
ORDER BY RS.RiskScoreId