MS-Access Query to PostgreSQL View - sql

I am converting a microsoft access query into a postgresql view. The query has obvious components that I have found reasonable answers to. However, I am still stuck on getting the final result:
SELECT All_Claim_Data.Sec_ID,
Sum(IIf([Type]="LODE",IIf([Status]="Active",1,0),0)) AS LD_Actv,
Sum(IIf([Type]="LODE",IIf([Loc_Date]>#8/31/2017#,IIf([Loc_Date]<#9/1/2018#,1,0),0),0)) AS LD_stkd_17_18,
Sum(IIf([Type]="LODE",IIf([Loc_Date]>#8/31/2016#,IIf([Loc_Date]<#9/1/2017#,1,0),0),0)) AS LD_stkd_16_17,
Sum(IIf([Type]="LODE",IIf([Loc_Date]<#1/1/1910#,IIf(IsNull([Clsd_Date]),1,(IIf([Clsd_Date]>#1/1/1900#,1,0))),0),0)) AS Actv_1900s,
Sum(IIf([Type]="LODE",IIf([Loc_Date]<#1/1/1920#,IIf(IsNull([Clsd_Date]),1,(IIf([Clsd_Date]>#1/1/1910#,1,0))),0),0)) AS Actv_1910s,
FROM All_Claim_Data.Sec_ID,
GROUP BY All_Claim_Data.Sec_ID,
HAVING (((Sum(IIf([casetype_txt]="LODE",1,0)))>0));
Realizing I need to use CASE SUM WHEN, here is what I have worked out so far:
CREATE OR REPLACE VIEW hgeditor.vw_test AS
SELECT All_Claim_Data.Sec_ID,
SUM (CASE WHEN(Type='LODE' AND WHEN(Status='Active',1,0),0)) AS LD_Actv,
SUM (CASE WHEN(Type='LODE' AND WHEN(Loc_Date>'8/31/2017' AND Loc_Date<'9/1/2018',1,0),0),0)) AS LD_stkd_17_18,
SUM (CASE WHEN(Type='LODE' AND WHEN(Loc_Date<'1/1/1910' AND (IsNull(Clsd_Date),1,(WHEN([Clsd_Date]>'1/1/1900',1,0))),0),0)) AS Actv_1900s
FROM All_Claim_Data.Sec_ID,
GROUP BY All_Claim_Data.Sec_ID,
HAVING (((SUM(IIf(Type='LODE',1,0)))>0));
The goal is to count the number of instances in which the Sec_ID has the following:
has (Type = LODE and Status = Active) = SUM integer
has (Type = LODE and Loc_Date between 8/31/2017 and 9/1/2018) = SUM Integer
My primary issue is getting a SUM integer to populate in the new columns

Case expressions are the equivalent to the Access IIF() functions, but WHEN isn't a function so it isn't used by passing a set of parameters. Think of it as being a tiny where clause instead, it evaluates one or more predicates to determine what to do, and the action taken is established by what you specify after THEN
CREATE OR REPLACE VIEW hgeditor.vw_test AS
SELECT
All_Claim_Data.Sec_ID
, SUM( CASE
WHEN TYPE = 'LODE' AND
STATUS = 'Active' THEN 1
ELSE 0
END ) AS LD_Actv
, SUM( CASE
WHEN TYPE = 'LODE' AND
Loc_Date > to_date('08/31/2017','mm/dd/yyyy') AND
Loc_Date < to_date('09/1/2018','mm/dd/yyyy') THEN 1
ELSE 0
END ) AS LD_stkd_17_18
, SUM( CASE
WHEN TYPE = 'LODE' AND
Loc_Date < to_date('1/1/1910','mm/dd/yyyy') AND
[Clsd_Date] > to_date('1/1/1900','mm/dd/yyyy') THEN 1
ELSE 0
END ) AS Actv_1900s
FROM All_Claim_Data.Sec_ID
GROUP BY
All_Claim_Data.Sec_ID
HAVING COUNT( CASE
WHEN Type = 'LODE' THEN 1
END ) > 0
;
By the way, you should NOT be relying on MM/DD/YYYY as dates in Postgres
nb: Aggregate functions ignore NULL, take this example:
+----------+
| id value |
+----------+
| 1 x |
| 2 NULL |
| 3 x |
| 4 NULL |
| 5 x |
+----------+
select
count(*) c_all
, count(value) c_value
from t
+-------+----------+
| c_all | c_value |
+-------+----------+
| 5 | 3 |
+-------+----------+
select
sum(case when value IS NOT NULL then 1 else 0 end) sum_case
, count(case when value IS NOT NULL then 1 end) count_case
from t
+----------+-------------+
| sum_case | count_case |
+----------+-------------+
| 3 | 3 |
+----------+-------------+

Related

How can I summarise this JSON array data as columns on the same row?

I have a PostgreSQL database with a table called items which includes a JSONB field care_ages.
This field contains an array of between one and three objects, which include these keys:
AgeFrom
AgeTo
Number
Register
For a one-off audit report I need to run on this table, I need to "unpack" this field into columns on the same row.
I've used jsonb_to_recordset to split it out into rows and columns, which gets me halfway:
SELECT
items.id,
items.name,
care_ages.*
FROM
ofsted_items,
jsonb_to_recordset(items.care_age) AS care_ages ("AgeFrom" integer, "AgeTo" integer, "Register" text, "MaximumNumber" integer)
This gives me output like:
| id | name | register | age_from | age_to | maximum_number |
|----|--------------|----------|----------|--------|----------------|
| 1 | namey mcname | xyz | 0 | 4 | 5 |
| 1 | namey mcname | abc | 4 | 8 | 7 |
Next, I need to combine these rows together, perhaps using GROUP BY, adding extra columns, like this:
| id | name | register_xyz? | xyz_age_from | xyz_age_to | xyz_maximum_number | register_abc? | abc_age_from | abc_age_to | abc_maximum_number |
|----|--------------|---------------|--------------|------------|--------------------|---------------|--------------|------------|--------------------|
| 1 | namey mcname | true | 0 | 4 | 5 | true | 4 | 8 | 7 |
Because I know ahead of time which "registers" there are (there's only three of them), it seems like this should be possible.
I've tried following this example, using CASE to calculate extra columns, but I'm not getting any useful values: all 0s and 5s for some reason.
If you are using Postgres 12 or later, you can use a jsonpath query to first extract the JSON object for each register into separate columns. Then use the "usualy" operators to extract the keys. This avoids first expanding into multiple rows just to aggregate them back into a single row later
select id, name,
(reg_xyz ->> 'AgeFrom')::int as xyz_age_from,
(reg_xyz ->> 'AgeTo')::int as xyz_age_to,
(reg_xyz ->> 'Number')::int as xyz_max_num,
(reg_abc ->> 'AgeFrom')::int as abc_age_from,
(reg_abc ->> 'AgeTo')::int as abc_age_to,
(reg_abc ->> 'Number')::int as abc_max_num
from (
select id, name,
jsonb_path_query_first(care_age, '$[*] ? (#.Register == "xyz")') as reg_xyz,
jsonb_path_query_first(care_age, '$[*] ? (#.Register == "abc")') as reg_abc
from ofsted_items
) t
At one point or another you will have to explicitly write out one expression for each column, so jsonb_to_recordset doesn't really buy you that much.
Online example
If you need this a lot, you can easily put this into a view.
Try below query:
select id,name,
max(case when register='xyz' then true end) as "register_xyz?",
max(case when register='xyz' then age_from end) as xyz_age_from,
max(case when register='xyz' then age_to end) as xyz_age_to,
max(case when register='xyz' then maximum_number end) as xyz_maximum_number,
max(case when register='abc' then true end) as "register_abc?",
max(case when register='abc' then age_from end) as abc_age_from,
max(case when register='abc' then age_to end) as abc_age_to,
max(case when register='abc' then maximum_number end) as abc_maximum_number
from (SELECT
items.id,
items.name,
care_ages.*
FROM
ofsted_items,
jsonb_to_recordset(items.care_age) AS care_ages ("AgeFrom" integer, "AgeTo" integer, "Register" text, "MaximumNumber" integer)
)t
group by id, name
You can use conditional aggregation to pivot your table. This can be done using the CASE clause as it was done at the solution you already linked or using the FILTER clause:
demo:db<>fiddle
SELECT
id,
name,
bool_and(true) FILTER (WHERE register = 'xyz') as "register_xyz?",
MAX(age_from) FILTER (WHERE register = 'xyz') as xyz_age_from,
MAX(age_to) FILTER (WHERE register = 'xyz') as xyz_age_to,
MAX(maximum_number) FILTER (WHERE register = 'xyz') as xyz_maximum_number,
bool_and(true) FILTER (WHERE register = 'abc') as "register_abc?",
MAX(age_from) FILTER (WHERE register = 'abc') as abc_age_from,
MAX(age_to) FILTER (WHERE register = 'abc') as abc_age_to,
MAX(maximum_number) FILTER (WHERE register = 'abc') as abc_maximum_number
FROM items,
jsonb_to_recordset(items.care_ages) AS care_ages ("age_from" integer, "age_to" integer, "register" text, "maximum_number" integer)
GROUP BY id, name

SQL Count of Column value and its a subColumn

I have A Table in DB2 Database such as below:
StatusCode | IsResolved | IsAssigned
ABC | Y |
ABC | N |
ABC | |
ADEF | Y |
ADEF | | Y
I want to get data in the way such as:
StatusCode |Count of Status Code| Count of Resolved with value Y| Count of Assigned With value Y
ABC | 3 | 1 | 0
ADEF | 2 | 1 | 1
I am able to get count of Status Code by using groupBy but I am not sure how to fetch data of count of resolved and assigned in the same query.
Query: select statusCode,count(statusCode) from table group by statusCode
Can anyone help me in how to fetch the resolved and Assigned count?
Issue Solution: Christian and JPW: Solution was to Use sum(case IsResolved when 'Y' then 1 else 0 end)
Try to use
select statusCode, count(statusCode),
sum(case IsResolved when 'Y' then 1 else 0 end),
sum(case IsAssigned when 'Y' then 1 else 0 end)
from table
group by statusCode
One way to get the result you want is to use conditional aggregation (where you use a predicate to determine how to aggregate data) like this:
select
StatusCode,
count(*) as "Count of Status Code",
sum(case when IsResolved = 'Y' then 1 else 0 end) as "Count of Resolved with value Y",
sum(case when IsAssigned = 'Y' then 1 else 0 end) as "Count of Assigned With value Y"
from your_table
group by StatusCode;
The case expression construct (case ... when ... then .. end) is part of the ANSI SQL standard, so this should work in any compliant database.
You can achieve this using SUM() and CASE
SELECT
statusCode,
COUNT(statusCode)
,SUM(CASE WHEN IsResolved='Y' THEN 1 ELSE 0 END) Resolved
,SUM(CASE WHEN IsAssigned='Y' THEN 1 ELSE 0 END) Assigned
FROM [Questions] GROUP BY statusCode
Here is a related question: Sql Server equivalent of a COUNTIF aggregate function
I suppose the prior answers used the SUM aggregate because the value of the missing values was unknown. If the missing values are the NULL value, then each could have been coded as the COUNT with the same effect as the SUM.
And if the missing values from the "I have a table" given in the OP are the NULL value, and if [effectively the data meets or actually there exists] a CHECK constraint for the isColumnNames of IN ('Y','N'), then similar to the other answers, but performing a COUNT and using NULLIF as a simplified/special-case effect of the CASE expression:
select
statuscode as "StatusCode"
, count(*) as "Count of Status Code"
, count(nullif(isResolved,'N')) as "Count of Resolved with value Y"
, count(nullif(isAssigned,'N')) as "Count of Assigned with value Y"
from so39705143
group by statuscode
order by statuscode

Compare version numbers in a SQL query

I have a table which is used for storing the compatibility for a specific version of a software. For example whether a version of the client is compatible with the backend. There is a lower and an upper bound, both have major, minor and revision version numbers. Upper bound numbers can be null (there is a check constraint which ensures that either all or none of them is null).
I'd like to create a query which returns the rows for various majorVersion, minorVersion and revisionVersion numbers.
Example (clientId left out to make it more simple):
minMajorVersion | minMinorVersion | minRevisionVersion | maxMajorVersion | maxMinorVersion | maxRevisionVersion
1 0 0 NULL NULL NULL
1 2 5 NULL NULL NULL
1 3 0 NULL NULL NULL
2 0 1 5 1 0
Let's say I want to know which client version is compatible with a backend version 1.2.6. For this, the query should return the first two rows, because the min versions are smaller, and the max versions are NULL.
For another backend version 2.0.1 the query should return the last row, and for backend version 5.2.0 the query should return nothing.
What I was able to create is this:
SELECT c.* FROM COMPATIBILITYQUALIFIER q
join client c on (c.id = q.clientid)
WHERE (q.MINBACKENDMAJORVERSION < 2
OR (q.MINBACKENDMAJORVERSION = 2 AND q.MINBACKENDMINORVERSION < 3)
OR (q.MINBACKENDMAJORVERSION = 2 AND q.MINBACKENDMINORVERSION = 3 AND q.MINBACKENDREVISIONVERSION <=6))
AND ((q.MAXBACKENDMAJORVERSION IS NULL)
OR ((q.MAXBACKENDMAJORVERSION > 2)
OR (q.MAXBACKENDMAJORVERSION = 2 AND q.MAXBACKENDMINORVERSION > 3)
OR (q.MAXBACKENDMAJORVERSION = 2 AND q.MAXBACKENDMINORVERSION = 3 AND q.MAXBACKENDREVISIONVERSION >= 6)))
order by c.MAJORVERSION DESC, c.MINORVERSION DESC, c.REVISIONVERSION DESC;
I don't think it would be performant.
An easy way to do this would be to create a stored procedure, but I don't want to put code in the DB right now.
Is there a way to do it with sub-queries? Anything else which is fast?
UPDATED.
Sure, the query is not the prettiest. But just because you have multiple conditional clauses like that doesn't mean that your query will be any slower.
Only as a matter of readability and to avoid repeating hardcoded values, I would rewrite the query to something like this:
select c.*
from compatibilityqualifier q
join (select 2 as major,
3 as minor,
6 as revision
from dual) ver
on 1=1
join client c
on c.id = q.clientid
where ver.major >= q.minBackendMajorVersion
and (ver.major > q.minBackendMajorVersion or ver.minor >= q.minBackendMinorVersion)
and (ver.major > q.minBackendMajorVersion or ver.minor > q.minBackendMinorVersion or ver.revision >= q.minBackendRevisionVersion)
and (q.maxBackendMajorVersion is null
or (ver.major <= q.maxBackendMajorVersion
and (ver.major < q.maxBackendMajorVersion or ver.minor <= q.maxBackendMinorVersion)
and (ver.major < q.maxBackendMajorVersion or ver.minor < q.maxBackendMinorVersion or ver.revision <= q.maxBackendRevisionVersion)
)
)
order by c.majorversion desc,
c.minorversion desc,
c.revisionversion desc
But I expect the performance to be pretty much identical.
For any given version number expressed as a tuple of (Major, Minor, Revision) you can use the following query to retrieve rows from your CompatibilityQualifier table. For example Version 1,2,6 below:
select q.*
from (select 1 major
, 2 minor
, 6 revision from dual) v
join CompatibilityQualifier q
on ( q.minMajorVersion < v.major or
( q.minMajorVersion = v.major and
( q.minMinorVersion < v.minor or
( q.minMinorVersion = v.minor and
q.minRevisionVersion <= v.revision))))
and ( q.maxMajorVersion is null or
q.maxMajorVersion > v.major or
( q.maxMajorVersion = v.major and
( q.MaxMinorVersion is null or
q.MaxMinorVersion > v.minor or
( q.MaxMinorVersion = v.minor and
( maxRevisionVersion is null or
q.maxRevisionVersion >= v.revision)))));
Which yields the following results:
| MINMAJORVERSION | MINMINORVERSION | MINREVISIONVERSION | MAXMAJORVERSION | MAXMINORVERSION | MAXREVISIONVERSION |
|-----------------|-----------------|--------------------|-----------------|-----------------|--------------------|
| 1 | 0 | 0 | (null) | (null) | (null) |
| 1 | 2 | 5 | (null) | (null) | (null) |
With revision 2,0,1 every row from CompatibilityQualifier would be returned since there are no upper bounds on any of the 1,x,x records.
If you really want records with NULL values of maxMajorVersion excluded from the result set when the queried major version number differs from the minMajorVersion then you can use this revised version:
select q.*
from (select 2 major
, 0 minor
, 1 revision from dual) v
join CompatibilityQualifier q
on ( q.minMajorVersion < v.major or
( q.minMajorVersion = v.major and
( q.minMinorVersion < v.minor or
( q.minMinorVersion = v.minor and
q.minRevisionVersion <= v.revision))))
and ( --q.maxMajorVersion is null or
q.maxMajorVersion > v.major or
( coalesce(q.maxMajorVersion -- When Null compare to minMajorVersion
,q.minMajorVersion) = v.major and
( q.MaxMinorVersion is null or
q.MaxMinorVersion > v.minor or
( q.MaxMinorVersion = v.minor and
( maxRevisionVersion is null or
q.maxRevisionVersion >= v.revision)))));
which just returns the one row:
| MINMAJORVERSION | MINMINORVERSION | MINREVISIONVERSION | MAXMAJORVERSION | MAXMINORVERSION | MAXREVISIONVERSION |
|-----------------|-----------------|--------------------|-----------------|-----------------|--------------------|
| 2 | 0 | 1 | 5 | 1 | 0 |
I know I am bit late to the party, but I found a very easy solution to compare versions in Oracle. Just need to make compare versions as varchar and compare.
Check the following simple query and tested it for different versions and it worked!!!
select case when '1.0.30' < '1.1.22' then 'true' else 'false' end as isVersionHigher
from dual;
--Result => 'true'
You can even compare versions just having major and minor digits. Check below query.
select case when '1.0' < '1.1.22' then 'true' else 'false' end as isVersionHigher
from dual;
--Result => 'true'

view data from database table

SELECT dun, COUNT( id_ahli ) AS JUMLAH_KESELURUHAN, COUNT(kaum='melayu') AS melayu, COUNT(kaum='cina') AS cina
FROM maklumat_ahli
WHERE jantina = 'lelaki'
AND
(kematian_tarikh IS NULL)
AND (bayaran_pertama IS NULL)
AND (bayaran_kedua IS NULL)
GROUP BY dun
ORDER BY dun
this is my sql statement. Is it posible to count and view data by kaum?. i use that sql statement, but my count is not correct
/-----------------------------------------/
|dun | Jumlah_keseluruhan | melayu | cina |
-------------------------------------------
|A |123 |100 |23 |
-------------------------------------------
is it any possible way to view data from db like the table above.
To count data by special value you may use CASE clause
COUNT(case when kaum='melayu' then 1 else 0 end) AS melayu,
COUNT(case when kaum='cina'the 1 else 0 end) AS cina

How to retrieve data from different rows of the same table based on different criteria

I'm trying to write a plain SQL statement for building an Oracle report but I'm stuck at some point. x_request table stores the requests made and different tasks related to specific requests that have been done are stored in x_request_work_log. To summarize the structure of these tables:
X_request
-id
-name
-requester
-request_date
x_request_work_log
-id
-request_id (foreign key)
-taskId
-start_date
-end_date
Now let's assume that these tables are filled with sample data as follows:
x_request
id name requester request_date
1 firstReq John 01/01/2012
2 secondReq Steve 21/01/2012
x_request_work_log
id requestId taskId startDate endDate
1 1 0 01/01/2012 03/01/2012
2 1 1 04/01/2012 04/01/2012
3 1 2 05/01/2012 15/01/2012
4 2 0 24/01/2012 02/02/2012
The template of my report is as follows:
requestName timeSpent(task(0)) timeSpent(task(1)) timeSpent(task(2))
| | | | | | | |
So, that's where I'm stuck. I need a Sql Select statement that will return each row in the formatted way as described above. How can i retrieve and display the start and end dates of different tasks. Btw timeSpent = endDate(task(x)) - startDate(task(x))
Note: Using different select subqueries for each spent time calculation is not an option due to performance constraints. There must be another way.
It sounds like you just want something like
SELECT r.name request_name,
SUM( (CASE WHEN l.taskId = 0
THEN l.endDate - l.StartDate
ELSE 0
END) ) task0_time_spent,
SUM( (CASE WHEN l.taskId = 1
THEN l.endDate - l.StartDate
ELSE 0
END) ) task1_time_spent,
SUM( (CASE WHEN l.taskId = 2
THEN l.endDate - l.StartDate
ELSE 0
END) ) task2_time_spent
FROM x_request_work_log l
JOIN x_request r ON (l.requestId = r.Id)
GROUP BY r.name
If you happen to be using 11g, you could also use the PIVOT operator.
If you need to display all members of a group in one row, you can accomplish this in MySQL with the GROUP_CONCAT operator (I don't know what the equivalent is in Oracle):
> SELECT requestID,
GROUP_CONCAT(DATEDIFF(endDate,startDate)) AS length
FROM request_work_log
GROUP BY requestId;
+-----------+--------+
| requestID | length |
+-----------+--------+
| 1 | 2,0,10 |
| 2 | 9 |
+-----------+--------+
(and then add in the inner join to your other table to replace requestID with the request name)