I have the following table:
Data nr1 nr2 nr3 nr4 nr5 nr6
2020-09-12 6 15 36 42 67 78
2020-09-10 46 48 67 78 80 87
2020-09-08 23 27 28 31 69 89
2020-09-05 7 14 27 56 72 83
2020-09-03 16 17 38 39 68 84
2020-09-01 10 22 28 45 48 71
2020-08-29 1 3 35 42 55 61
2020-08-27 37 49 52 53 75 87
2020-08-25 15 24 31 70 83 84
2020-08-22 7 12 45 47 73 87
2020-08-20 7 17 30 39 41 67
2020-08-18 13 22 28 58 65 77
2020-08-17 5 9 26 62 77 79
2020-08-13 4 5 49 57 66 75
2020-08-11 7 9 38 68 78 80
2020-08-08 6 16 22 55 58 83
2020-08-06 21 37 40 46 69 80
2020-08-04 5 19 21 25 45 82
2020-08-01 4 14 17 18 26 45
2020-07-30 4 15 19 26 28 55
2020-07-28 23 45 49 71 80 82
2020-07-25 18 30 42 70 78 80
2020-07-23 10 29 37 49 56 57
2020-07-21 4 34 46 54 55 62
2020-07-18 18 33 49 76 80 84
I have to do the following task:
Select into a #TEMP table with only one column DistinctNumbers all distinct numbers of the above table because some numbers in the above table might be repeated across rows and columns.
Select into another #TEMP table all numbers in the range from 1 to 99 which are not in the original table.
What is the best way of accomplishing these two tasks?
You should unpivot original table first
1.Unpivot original table into #temp table
2.Now you have all numbers in one column
3.Use while between 1 and 99 and insert counter into #RESULT table where not in #temp(unpivoted table)
SELECT DISTINCT(num) num INTO #TEMP_DISTINCT_NUMBERS FROM ORIGINAL_TABLE UNPIVOT (
num
FOR PivotColumn IN (nr1,nr2,nr3,nr4,nr5,nr6)
) AS UNPIVOTE_TABLE
CREATE TABLE #RESULT(NUM INT)
DECLARE #COUNTER INT =1;
WHILE(#COUNTER<=99)
BEGIN
INSERT INTO #RESULT SELECT #COUNTER WHERE #COUNTER NOT IN (SELECT num FROM
#TEMP_DISTINCT_NUMBERS)
SET #COUNTER=#COUNTER+1
END
SELECT * FROM #RESULT
you can try this:
;WITH tally
AS (SELECT 1 AS num
UNION ALL
SELECT num + 1
FROM tally
WHERE num < 99)
SELECT DISTINCT tally.num
FROM tally
LEFT JOIN
( SELECT num FROM #dataset --your dataset
CROSS APPLY (VALUES (nr1),(nr2),(nr3),(nr4),(nr5),(nr6)) AS B (num)
) AS dataset
ON tally.num = dataset.num
WHERE dataset.num IS NULL
Code above:
Create [tally] recursive common table expression with sequence from 1 to 99
Left join tally with your unpivoted dataset ...
test here: https://rextester.com/YEB57637
I have one or more start and end periods which can be consecutive, overlap, or anything in between. My goal is to have all 12 months displayed regardless of when the period starts; that is, I can see months before the period, during the period and potentially after the period. For sake of this example, I'm checking it against 2019 so I want to see all 12 months filled in for 2019.
I have the following sample data to illustrate the problem:
DECLARE #DATES TABLE (ID int, EffectiveDate date, EffectiveEndDate date)
INSERT INTO #DATES
VALUES
(43, '2018-10-01', '2019-09-30'),
(43, '2019-10-01', '2020-09-30'),
(44, '2019-10-01', '2020-09-30');
I also have a "tally" table which has all 12 months and the start of the month (omitted for brevity but it's a temp table with a column called N which has a value of 1-12 representing the month, and a column StartOfMonth which is the start date of the month. Now what I want is to have each ID (43 and 44 in this case) show all 12 months. This is easy with 43 where there are two records that run from October 2018 to November 2020, as it falls within all 12 months. 44 however only gives me October, November and December since there is only one row that begins in October. I cannot add a row for the previous months.
The Months table is simply defined as the follows:
DROP TABLE IF EXISTS #Months;
CREATE TABLE #Months (N tinyint, StartOfMonth date);
INSERT INTO #Months
VALUES
(1, DATEFROMPARTS(2019, 1, 1)),
(2, DATEFROMPARTS(2019, 2, 1)),
(3, DATEFROMPARTS(2019, 3, 1)),
(4, DATEFROMPARTS(2019, 4, 1)),
(5, DATEFROMPARTS(2019, 5, 1)),
(6, DATEFROMPARTS(2019, 6, 1)),
(7, DATEFROMPARTS(2019, 7, 1)),
(8, DATEFROMPARTS(2019, 8, 1)),
(9, DATEFROMPARTS(2019, 9, 1)),
(10, DATEFROMPARTS(2019, 10, 1)),
(11, DATEFROMPARTS(2019, 11, 1)),
(12, DATEFROMPARTS(2019, 12, 1));
Code:
SELECT Month = m.N,
d.ID,
d.EffectiveDate,
d.EffectiveEndDate,
-- This flag doesn't mean anything, just so I can better see the results I'm getting
Ind = CASE
WHEN m.StartOfMonth BETWEEN d.EffectiveDate AND d.EffectiveEndDate
THEN 1
ELSE 0
END
FROM #dates d
LEFT JOIN #Months m
ON m.N BETWEEN 1 AND 12
WHERE
m.StartOfMonth
BETWEEN EffectiveDate AND EffectiveEndDate
ORDER BY ID, m.N;
This gives me the following (wrong) output:
Month ID EffectiveDate EffectiveEndDate Ind
1 43 2018-10-01 2019-09-30 1
2 43 2018-10-01 2019-09-30 1
3 43 2018-10-01 2019-09-30 1
4 43 2018-10-01 2019-09-30 1
5 43 2018-10-01 2019-09-30 1
6 43 2018-10-01 2019-09-30 1
7 43 2018-10-01 2019-09-30 1
8 43 2018-10-01 2019-09-30 1
9 43 2018-10-01 2019-09-30 1
10 43 2019-10-01 2020-09-30 1
11 43 2019-10-01 2020-09-30 1
12 43 2019-10-01 2020-09-30 1
!!! THIS PART IS WRONG !!!
10 44 2019-10-01 2020-09-30 1
11 44 2019-10-01 2020-09-30 1
12 44 2019-10-01 2020-09-30 1
If I skip the effective date/effective end date check or try to do some sort of case statement where I say if month begins before the effective date then include it anyway 43 doubles up on the months because there are two rows, while 44 works as expected.
What I need is to get this:
Month ID EffectiveDate EffectiveEndDate Ind
1 43 2018-10-01 2019-09-30 1
2 43 2018-10-01 2019-09-30 1
3 43 2018-10-01 2019-09-30 1
4 43 2018-10-01 2019-09-30 1
5 43 2018-10-01 2019-09-30 1
6 43 2018-10-01 2019-09-30 1
7 43 2018-10-01 2019-09-30 1
8 43 2018-10-01 2019-09-30 1
9 43 2018-10-01 2019-09-30 1
10 43 2019-10-01 2020-09-30 1
11 43 2019-10-01 2020-09-30 1
12 43 2019-10-01 2020-09-30 1
1 44 2019-10-01 2020-09-30 0
2 44 2019-10-01 2020-09-30 0
3 44 2019-10-01 2020-09-30 0
4 44 2019-10-01 2020-09-30 0
5 44 2019-10-01 2020-09-30 0
6 44 2019-10-01 2020-09-30 0
7 44 2019-10-01 2020-09-30 0
8 44 2019-10-01 2020-09-30 0
9 44 2019-10-01 2020-09-30 0
10 44 2019-10-01 2020-09-30 1
11 44 2019-10-01 2020-09-30 1
12 44 2019-10-01 2020-09-30 1
where all 12 months are showing for all situations, whether there's consecutive ranges or one range that starts at any given point in the year.
There's probably a better way to do this, but here is an ugly solution:
-- Build base data
DECLARE #DATES TABLE (ID int, EffectiveDate date, EffectiveEndDate date)
INSERT INTO #dates
VALUES
(43, '2018-10-01', '2019-09-30'),
(43, '2019-10-01', '2020-09-30'),
(44, '2019-10-01', '2020-09-30');
DECLARE #months TABLE (StartOfMonth date, n int)
;WITH dateCTE
AS
(
SELECT ROW_NUMBER() OVER (ORDER BY number) - 1 AS rn
FROM master.dbo.spt_values
)
INSERT #months (StartOfMonth, n)
SELECT CAST(DATEADD(mm, rn, '2018-01-01') AS date) AS StartOfMonth, DATEPART(mm,DATEADD(mm, rn, '2018-01-01')) AS n
FROM dateCTE
WHERE rn < 48
-- build a list of all IDs and months where the ID is active in the year
;with dateCTE
AS
(
SELECT DISTINCT d.ID, m.StartOfMonth, m.n
FROM #months AS m
CROSS
JOIN #dates AS d
WHERE DATEPART(YEAR,m.StartOfMonth) BETWEEN DATEPART(YEAR,d.EffectiveDate) and DATEPART(YEAR,d.EffectiveEndDate)
)
-- join list from previous step to the activity data
-- this generates the full list with NULLs where the ID was not active
,listCTE
AS
(
SELECT cd.ID, cd.StartOfMonth, cd.n, d.EffectiveDate, d.EffectiveEndDate
FROM dateCTE AS cd
LEFT
JOIN #dates AS d
ON d.ID = cd.ID
AND cd.StartOfMonth between d.EffectiveDate AND d.EffectiveEndDate
)
-- fill in the NULLS by joining the table back to itelf
SELECT n AS [Month],
ID,
COALESCE(EffectiveDate,
(SELECT TOP 1 EffectiveDate FROM listCTE AS l2 WHERE l2.ID = l.ID AND l2.EffectiveDate > l.StartOfMonth ORDER BY l2.StartOfMonth DESC),
(SELECT TOP 1 EffectiveDate FROM listCTE AS l2 WHERE l2.ID = l.ID AND l2.EffectiveEndDate < l.StartOfMonth ORDER BY l2.StartOfMonth DESC)
) AS EffectiveDate,
COALESCE(EffectiveEndDate,
(SELECT TOP 1 EffectiveEndDate FROM listCTE AS l2 WHERE l2.ID = l.ID AND l2.EffectiveDate > l.StartOfMonth ORDER BY l2.StartOfMonth DESC),
(SELECT TOP 1 EffectiveEndDate FROM listCTE AS l2 WHERE l2.ID = l.ID AND l2.EffectiveEndDate < l.StartOfMonth ORDER BY l2.StartOfMonth DESC)
) AS EffectiveEndDate,
CASE
WHEN StartOfMonth BETWEEN EffectiveDate AND EffectiveEndDate
THEN 1
ELSE 0
END AS Ind,
StartOfMonth
FROM listCTE AS l
WHERE DATEPART(YEAR,StartOfMonth) = 2019
ORDER BY ID, StartOfMonth
(This code uses a #months table variable rather than the #months temp table in the original)
This works by building a list of all IDs and months, then left-joining that to the #dates table to generate the months where each ID is active. Finally, the second result set is joined back to itself to fill in the NULLs.
This is likely to have horrible performance when applied to data at scale; it might be possible to mitigate this by materialising the interim steps of the CTEs into tables (or temp tables) with appropriate indexes.
I am trying to use the built-in filter function in PostgreSQL to filter for a date range in order to sum only entries falling within this time-frame.
I cannot understand why the filter isn't being applied.
I am trying to filter for all product transactions that have a created_at date of the previous month (so in this case that were created in June 2017).
SELECT pt.created_at::date, pt.customer_id,
sum(pt.amount/100::double precision) filter (where (date_part('month', pt.created_at) =date_part('month', NOW() - interval '1 month') and
date_part('year', pt.created_at) = date_part('year', NOW()) ))
from
product_transactions pt
LEFT JOIN customers c
ON c.id= pt.customer_id
GROUP BY pt.created_at::date,pt.customer_id
Please find my expected results (sum of the amount for each day in the previous month - for each customer_id if an entry for that day exists) and the actual results I get from the query - below (using date_trunc).
Expected results:
created_at| customer_id | amount
2017-06-30 1 220.5
2017-06-28 15 34.8
2017-06-28 12 157
2017-06-28 48 105.6
2017-06-27 332 425.8
2017-06-25 1 58.0
2017-06-25 23 22.5
2017-06-21 14 88.9
2017-06-17 2 34.8
2017-06-12 87 250
2017-06-05 48 135.2
2017-06-05 12 95.7
2017-06-01 44 120
Results:
created_at| customer_id | amount
2017-06-30 1 220.5
2017-06-28 15 34.8
2017-06-28 12 157
2017-06-28 48 105.6
2017-06-27 332 425.8
2017-06-25 1 58.0
2017-06-25 23 22.5
2017-06-21 14 88.9
2017-06-17 2 34.8
2017-06-12 87 250
2017-06-05 48 135.2
2017-06-05 12 95.7
2017-06-01 44 120
2017-05-30 XX YYY
2017-05-25 XX YYY
2017-05-15 XX YYY
2017-04-30 XX YYY
2017-03-02 XX YYY
2016-11-02 XX YYY
The actual results give me the sum for all dates in the database, so no date time-frame is being applied in the query for a reason I cannot understand. I'm seeing dates that are both not for June 2017 and also from previous years.
Use date_trunc(..) function:
SELECT pt.created_at::date, pt.customer_id, c.name,
sum(pt.amount/100::double precision) filter (where date_trunc('month', pt.created_at) = date_trunc('month', NOW() - interval '1 month'))
from
product_transactions pt
LEFT JOIN customers c
ON c.id= pt.customer_id
GROUP BY pt.created_at::date
I created a view which is presently giving the data like this:
practice_name message_type message_count
CHC ALOG_SYNC 1
CHC BULKNT 0
CHC PIE_SYNC 1
CHC PPRV_SYNC 1
CHC SYNC_PRACT 3
CHC SYNC_PROV 9
CHC SYNC_WTXT 3
CHC SYNC_XYZ 0
Midtown ALOG_SYNC 0
Midtown BULKNT 0
Midtown PIE_SYNC 0
Midtown PPRV_SYNC 0
Midtown SYNC_PRACT 3
Midtown SYNC_PROV 0
Midtown SYNC_WTXT 3
Midtown SYNC_XYZ 0
NextGen MedicalPractice ALOG_SYNC 0
NextGen MedicalPractice BULKNT 1
NextGen MedicalPractice PIE_SYNC 0
NextGen MedicalPractice PPRV_SYNC 0
NextGen MedicalPractice SYNC_PRACT 3
NextGen MedicalPractice SYNC_PROV 591
NextGen MedicalPractice SYNC_WTXT 3
NextGen MedicalPractice SYNC_XYZ 0
My View:
CREATE OR REPLACE VIEW sha.sha_export_queue_view AS
SELECT q3.practice_name,
q3.message_type,
q3.share_site_org_key,
COALESCE(q2.message_count, '0'::text) AS message_count
FROM ( SELECT q1.practice_name,
mt.message_type,
q1.share_site_org_key
FROM sha.message_types mt,
( SELECT DISTINCT jsonb_array_elements((ai.result_json -> 'Patient Portal Operational Information'::text) -> 'nxmd_export contents by message type'::text) ->> 'Practice Name'::text AS practice_name,
ai.share_site_org_key
FROM sha.sha_share_site_view ssv
LEFT JOIN ( SELECT mytable2.assessment_id,
mytable2.result_json,
mytable2.share_site_org_key,
mytable2.rnk
FROM ( SELECT assessment_info.assessment_id,
assessment_info.result_json,
assessment_info.share_site_org_key,
dense_rank() OVER (PARTITION BY assessment_info.share_site_org_key ORDER BY assessment_info.modified_datetime DESC) AS rnk
FROM sha.assessment_info
WHERE assessment_info.assessment_id = 8::numeric) mytable2
WHERE mytable2.rnk = 1) ai ON ssv.share_site_org_key = ai.share_site_org_key) q1) q3
LEFT JOIN ( SELECT jsonb_array_elements((ai.result_json -> 'Patient Portal Operational Information'::text) -> 'nxmd_export contents by message type'::text) ->> 'Practice Name'::text AS practice_name,
jsonb_array_elements((ai.result_json -> 'Patient Portal Operational Information'::text) -> 'nxmd_export contents by message type'::text) ->> 'Message Type'::text AS message_type,
jsonb_array_elements((ai.result_json -> 'Patient Portal Operational Information'::text) -> 'nxmd_export contents by message type'::text) ->> 'Message Count'::text AS message_count
FROM sha.sha_share_site_view ssv
LEFT JOIN ( SELECT mytable2.assessment_id,
mytable2.result_json,
mytable2.share_site_org_key,
mytable2.rnk
FROM ( SELECT assessment_info.assessment_id,
assessment_info.result_json,
assessment_info.share_site_org_key,
dense_rank() OVER (PARTITION BY assessment_info.share_site_org_key ORDER BY assessment_info.modified_datetime DESC) AS rnk
FROM sha.assessment_info
WHERE assessment_info.assessment_id = 8::numeric) mytable2
WHERE mytable2.rnk = 1) ai ON ssv.share_site_org_key = ai.share_site_org_key) q2 ON q3.message_type::text = q2.message_type AND q3.practice_name = q2.practice_name
ORDER BY q3.practice_name;
I want the second column to be flattened:
Practice Time Stamp <<message type 1>> <<message type 2>> <<message type 3>> <<message type 4 >> <<message type 5>> <<message type 6>> <<message type 7>> <<message type 8>>
Practice Name 1 21-12-2016 10:00 23 25 27 29 31 33 35 37
Practice Name 2 21-12-2016 10:00 24 26 28 30 32 34 36 38
Practice Name 3 21-12-2016 13:00 25 27 29 31 33 35 37 39
Practice Name 4 21-12-2016 13:00 26 28 30 32 34 36 38 40
Practice Name 5 24-12-2016 13:00 27 29 31 33 35 37 39 41
Practice Name 6 27-12-2016 13:00 28 30 32 34 36 38 40 42
Practice Name 7 30-12-2016 13:00 29 31 33 35 37 39 41 43
Practice Name 8 02-01-2017 13:00 30 32 34 36 38 40 42 44
Practice Name 1 05-01-2017 13:00 31 33 35 37 39 41 43 45
Practice Name 2 08-01-2017 13:00 32 34 36 38 40 42 44 46
Practice Name 3 11-01-2017 13:00 33 35 37 39 41 43 45 47
Is there any way I can achieve that?
Sorry for the little alignment issue.
The values are corresponding message type values
Sample for query (idea in comments to question):
SELECT
practice_name,
sum("ALOG_SYNC") AS "ALOG_SYNC",
sum("BULKNT") AS "BULKNT",
...
FROM (
SELECT
practice_name,
CASE WHEN q3.message_type = 'ALOG_SYNC' THEN sum(message_count) END AS "ALOG_SYNC",
CASE WHEN q3.message_type = 'BULKNT' THEN sum(message_count) END AS "BULKNT"
FROM
<your from + where clause>
) AS A
GROUP BY 1
Probably your query might be optimised.
Or you can use crosstab function (https://www.postgresql.org/docs/current/static/tablefunc.html)
For this question:
Get the pids of products ordered through any agent who makes at least one order for a customer in Kyoto. Use joins this time; no sub-queries.
I was able to get the answer using a subquery:
select distinct pid
from orders
where aid in (
select aid
from orders
where cid in(
select cid
from customers
where city = 'Kyoto'
)
)
I cannot figure out how to do this using only joins however.
This code returns the aid's that i need to get the pid's however i can't come up with a way to get them without using a sub query:
select distinct o.aid
from orders o, customers c
where o.cid = c.cid
and c.city = 'Kyoto'
Here are the two tables i am using:
Customers:
cid name city discount
c001 Tiptop Duluth 10.00
c002 Basics Dallas 12.00
c003 Allied Dallas 8.00
c004 ACME Duluth 8.00
c005 Weyland-Yutani Acheron 0.00
c006 ACME Kyoto 0.00
and Orders:
ordno mon cid aid pid qty dollars
1011 jan c001 a01 p01 1000 450.00
1013 jan c002 a03 p03 1000 880.00
1015 jan c003 a03 p05 1200 1104.00
1016 jan c006 a01 p01 1000 500.00
1017 feb c001 a06 p03 600 540.00
1018 feb c001 a03 p04 600 540.00
1019 feb c001 a02 p02 400 180.00
1020 feb c006 a03 p07 600 600.00
1021 feb c004 a06 p01 1000 460.00
1022 mar c001 a05 p06 400 720.00
1023 mar c001 a04 p05 500 450.00
1024 mar c006 a06 p01 800 400.00
1025 apr c001 a05 p07 800 720.00
1026 may c002 a05 p03 800 740.00