Select Total and If Read - sql

I have the following tables:
Book
Id Title
1 Test
BookPage (BookId corresponds to Id from Book table)
Id BookId Page
1 1 1
1 1 2
BookUserPage (BookPageId corresponds to Id from BookPage table)
UserId BookPageId
1 1
2 2
3 1
My select query is as follows:
SELECT B.[Id], B.[Title], BP.[Id], BP.[BookId], BP.[Page], COUNT(BUP.[BookpageId]) AS Total
FROM [Book] B
LEFT OUTER JOIN [BookPage] BP ON BP.[BookId] = B.[Id]
LEFT OUTER JOIN [BookUserPage] BUP ON BUP.[BookPageId] = BP.[Id]
WHERE B.[Id] = 1
GROUP BY B.[Id], B.[Title], BP.[Id], BP.[BookId], BP.[Page]
The result I get is the following:
Id, Title, Id, BookId, Page, Total
1 Test 1, 1, 1, 2
1 Test 2, 1, 1, 1
I'm trying to modify the query so that it will also tell me which of the 2 pages were read by the user.
I have tried the following:
SELECT B.[Id], B.[Title], BP.[Id], BP.[BookId], BP.[Page], COUNT(BUP.[BookpageId]) AS Total,
CASE WHEN EXISTS (
SELECT BUP2.[UserId]
FROM [PollUserAnswer] BUP2
WHERE BUP2.[UserId] = '98ad813b-cd0e-4a63-b40a-e09ee84f4b96')
THEN 1
ELSE 0
END AS Voted
FROM [Book] B
LEFT OUTER JOIN [BookPage] BP ON BP.[BookId] = B.[Id]
LEFT OUTER JOIN [BookUserPage] BUP ON BUP.[BookPageId] = BP.[Id]
WHERE B.[Id] = 1
GROUP BY B.[Id], B.[Title], BP.[Id], BP.[BookId], BP.[Page]
But the above puts a 1 on both rows of my result. I have also tried adding in the Case statement a condition:
AND BUP2.[BookPageId] = BUP.[PageId]
But that can't work due to group by and i can't list it in the group by as its a subquery.
My Desired Output for user 1 and 3 is this:
Id, Title, Id, BookId, Page, Total, Read
1 Test 1, 1, 1, 2, 1
1 Test 2, 1, 1, 1, 0
My Desired Output for user 2 is this:
Id, Title, Id, BookId, Page, Total, Read
1 Test 1, 1, 1, 2, 0
1 Test 2, 1, 1, 1, 1
Note: Please ignore the fact that there are 2 Id columns in the query output.

I would join to separate subquery to find the total number of users who read a given page. Then, you only need to tag on another left join to BookUserPage to generate the Read column:
SELECT b.Id, b.Title, bp.Id, bp.BookId, bp.Page, bup1.total,
CASE WHEN bup2.UserId IS NULL THEN 0 ELSE 1 END AS [Read]
FROM Book b
LEFT JOIN BookPage bp
ON bp.BookId = b.Id
LEFT JOIN
(
SELECT BookPageId, COUNT(*) AS total
FROM BookUserPage
GROUP BY BookPageId
) bup1
ON bup1.BookPageId = bp.Id
LEFT JOIN BookUserPage bup2
ON bup2.BookPageId = bp.Id AND bup2.UserId = 1
WHERE b.Id = 1;
Demo
This is not generating the results you expect, but it seems logically correct given your actual sample data and the join logic.

Related

sqlite return same value on left join

in below my sqlite command i want to get count of barcoeds when that's equals with sessions.id
select sessions.id, sessions.session_name, sessions.session_type,sessions.date_time, count(barcodes.id) as barcode_count
from sessions left join barcodes
on sessions.id = barcodes.session_id
group by barcodes.id
order by sessions.id desc
this command works, but that return more data with same value, for example if data is one, that return more than 3, but really i have one row
0 = {_List} size = 5
0 = 11
1 = "111"
2 = 2
3 = "1398/05/14 ساعت: 08:43"
4 = 1
1 = {_List} size = 5
0 = 11
1 = "111"
2 = 2
3 = "1398/05/14 ساعت: 08:43"
4 = 1
2 = {_List} size = 5
0 = 11
1 = "111"
2 = 2
3 = "1398/05/14 ساعت: 08:43"
4 = 1
I think you want one row per session. So, your query is aggregating by the wrong column:
select s.id, s.session_name, s.session_type,
s.date_time, count(b.id) as barcode_count
from sessions s left join
barcodes b
on s.id = b.session_id
group by s.id
---------^ sessions not barcode
order by s.id desc;
You might find this also easy to do with a correlated subquery:
select s.*,
(select count(*) from barcodes b where b.session_id = s.id)
from sessions s;
The use of table aliases in these queries makes them easier to write and to read.
First count the ids for each session_id in table barcodes and then join to sessions:
select
s.id, s.session_name, s.session_type, s.date_time,
coalesce(b.barcode_count, 0) barcode_count
from sessions s left join (
select session_id, count(id) barcode_count
from barcodes
group by session_id
) b on s.id = b.session_id
order by s.id desc
I guess id is unique in the table barcodes so there is no need for count(distinct id).

Insert a batch number into a table in Oracle SQL

I have a temp table where I insert modified data extracted from a SELECT query.
In this temp table I want to group my rows into batches, so I added an indexed INT column called "BATCH_NUM"
The idea that I am hoping to achieve is this (for say 1000 results in my SELECT statement).
Pseudo Code
Batch Size = 100
Count = 0
For batch size in results set
Insert Into Temp Table (a , b , y , count)
Count++
Current SQL - inputs static value of 1 into BATCH_NUM column
INSERT INTO TEMP_TABLE
(
ASSET_ID,
PAR_PROM_INTEG_ID,
IGNORE
BATCH_NUM
)
SELECT carelevel.row_id, pstn.PROM_INTEG_ID,
CASE
WHEN promoprod.fabric_cd = 'Disabled'
THEN 'Y'
ELSE 'N'
END
'1'
FROM SIEBEL.S_ASSET carelevel
INNER JOIN SIEBEL.S_ASSET pstn
ON pstn.row_id = carelevel.par_asset_id
INNER JOIN SIEBEl.S_ASSET promotion
ON pstn.prom_integ_id = promotion.integration_id
INNER JOIN SIEBEL.S_PROD_INT prod
ON prod.row_id = carelevel.prod_id
INNER JOIN SIEBEL.S_ORG_EXT bill
ON carelevel.bill_accnt_id = bill.row_id
INNER JOIN SIEBEL.S_INV_PROF invoice
ON bill.row_id = invoice.accnt_id
INNER JOIN SIEBEL.S_PROD_INT promoprod
ON promotion.prod_id = promoprod.row_id
WHERE prod.part_num = 'Testproduct'
But if the select statement has 1000 results, then I want BATCH_NUM to go from 1,2,3,4,5,6,7,8,9,10 per 100 records.
Can this be done?
To map record to batch, you might simply want to use integer division. Or slightly more complicated as row are numbered from 1, but something like TRUNC((ROWNUM-1)/100)+1 will do the trick.
Here is a test for that mapping:
select level, trunc((ROWNUM-1)/100)+1 from dual connect by level <= 1000
Result:
ROWNUM TRUNC((ROWNUM-1)/100)+1
1 1
...
100 1
101 2
...
200 2
201 3
...
...
900 9
901 10
...
1000 10
Given your query:
INSERT INTO TEMP_TABLE
(
ASSET_ID,
PAR_PROM_INTEG_ID,
IGNORE,
BATCH_NUM
)
SELECT carelevel.row_id, pstn.PROM_INTEG_ID,
CASE
WHEN promoprod.fabric_cd = 'Disabled'
THEN 'Y'
ELSE 'N'
END,
TRUNC((ROWNUM-1)/100)+1,
-- ^^^^^^^^^^^^^^^^^^^^
-- map rows 1-100 to batch 1, rows 101-200 to batch 2 and so on
FROM SIEBEL.S_ASSET carelevel
INNER JOIN SIEBEL.S_ASSET pstn
ON pstn.row_id = carelevel.par_asset_id
INNER JOIN SIEBEl.S_ASSET promotion
ON pstn.prom_integ_id = promotion.integration_id
INNER JOIN SIEBEL.S_PROD_INT prod
ON prod.row_id = carelevel.prod_id
INNER JOIN SIEBEL.S_ORG_EXT bill
ON carelevel.bill_accnt_id = bill.row_id
INNER JOIN SIEBEL.S_INV_PROF invoice
ON bill.row_id = invoice.accnt_id
INNER JOIN SIEBEL.S_PROD_INT promoprod
ON promotion.prod_id = promoprod.row_id
WHERE prod.part_num = 'Testproduct'

Showing specific output data based on duplicate rows and null values [postgresql]

I'm using the following SQL (with a union to two similar queries):
SELECT
distinct a.source,
a.p_id,
a.name,
b.prod_count,
b.prod_amt,
'Def' as prod_type
FROM
dwh.attribution_product_count a
LEFT OUTER JOIN
(
SELECT
distinct source,
p_id,
name,
sum(acct_count) as prod_count,
sum(acct_amt) as prod_amt
FROM
dwh.prod_count
WHERE
month = 3 AND
default_banner_flag = 0 AND
loan_flag = 3
GROUP BY
source,
name,
p_id ) as b
ON
a.p_id = b.p_id
UNION
SELECT
distinct a.source,
a.p_id,
a.name,
b.prod_count,
b.prod_amt,
'Other' as prod_type
FROM
dwh.attribution_product_count a
LEFT OUTER JOIN
(
SELECT
distinct source,
p_id,
name,
sum(acct_count) as prod_count,
sum(acct_amt) as prod_amt
FROM
dwh.prod_count
WHERE
month = 3 AND
default_banner_flag = 1 AND
loan_flag = 3
GROUP BY
source,
name,
p_id
ORDER BY
name ) as b
ON
a.p_id = b.p_id
The output I'm getting looks like this:
Essentially since FakeName #2 has one row showing actual numbers (not null), I ONLY want FakeName #2 to show up. This means I also want the null row for FakeName #2. But, since FakeName #1 and #3 have 2 null rows, I don't need them to show. What type of SQL command (or edit to my query) can accomplish this?
Firstly, if I read your query correctly, you can eliminate the need for a UNION by using CASE and IN. You also have a couple of bogus DISTINCTs in there (since you're using GROUP BY anyway). That gives:
SELECT DISTINCT
a.source,
a.p_id,
a.name,
b.prod_count,
b.prod_amt,
Case When default_banner_flag = 0 Then 'Def' Else 'Other' End as prod_type
FROM
dwh.attribution_product_count a
LEFT OUTER JOIN
(
SELECT
source,
p_id,
name,
default_banner_flag,
sum(acct_count) as prod_count,
sum(acct_amt) as prod_amt
FROM
dwh.prod_count
WHERE
month = 3 AND
default_banner_flag in (0, 1) AND
loan_flag = 3
GROUP BY
source,
name,
p_id,
default_banner_flag
) as b
ON
a.p_id = b.p_id
However, what you actually want is information about those p_ids which have at least one row in dwh.prod_count, so I think you can change your whole query around to use that as the sub-select:
SELECT
a.source,
a.p_id,
a.name,
sum(acct_count) as prod_count,
sum(acct_amt) as prod_amt,
Case When default_banner_flag = 0 Then 'Def' Else 'Other' End as prod_type
FROM
dwh.attribution_product_count a
LEFT OUTER JOIN
dwh.prod_count b
On a.p_id = b.p_id
INNER JOIN
(
SELECT DISTINCT
p_id
FROM
dwh.prod_count
WHERE
month = 3 AND
default_banner_flag in (0, 1) AND
loan_flag = 3
) as c
ON a.p_id = c.p_id
WHERE
month = 3 AND
default_banner_flag in (0, 1) AND
loan_flag = 3
(You could also rewrite this as a WHERE p_id IN ( sub-select ) or with a little fiddling WHERE EXISTS ( ... ), but this seemed the easiest version to demonstrate.)
Note that I haven't actually tested any of these queries, but I think they're logically sound.

Join 2 tables: one data table and one table of statut and get statut with no entrie

I have this query:
SELECT c.Show_Code, req.Statut_ID, COUNT(req.Statut_ID) 'Count'
FROM [Case] c
JOIN Request req ON req.Case_Number = c.Number
GROUP BY c.Show_Code, req.Statut_ID
The result is:
Show_Code Statut_ID Count
564900 2 1
568127 2 1
And I have this statut table (Ref_RequestStatut)
ID Name
1 Test
2 Test2
How can I get this result:
Show_Code Statut_ID Count
564900 1 0
564900 2 1
568127 1 0
568127 2 1
I want all the statut, even those which have no value?
Thank
If you are using SQL Server 2005 or later:
WITH counted AS (
SELECT c.Show_Code, req.Statut_ID, COUNT(req.Statut_ID) 'Count'
FROM [Case] c
JOIN Request req ON req.Case_Number = c.Number
GROUP BY c.Show_Code, req.Statut_ID
),
showcodes AS (
SELECT DISTINCT Show_Code
FROM counted
)
SELECT
s.Show_Code,
r.ID AS Statut_ID,
Count = ISNULL(c.Count, 0)
FROM showcodes s
CROSS JOIN Ref_RequestStatut r
LEFT JOIN counted c ON s.Show_Code = c.Show_Code AND r.ID = c.Statut_ID
ORDER BY
s.Show_Code,
r.ID

SELECT Data from multiple tables?

I have 3 tables, with 3 fields all the same. I basically want to select information from each table
For example:
userid = 1
I want to select data from all 3 tables, where userid = 1
I am currently using:
SELECT r.*,
p.*,
l.*
FROM random r
LEFT JOIN pandom p ON r.userid = p.userid
LEFT JOIN landom l ON l.userid = r.userid
WHERE r.userid = '1'
LIMIT 0, 30
But it doesn't seem to work.
with 3 fields all the same
So you mean that you want the same 3 fields from all 3 tables?
SELECT r.col1, r.col2, r.col3
FROM random r
WHERE r.userid = '1'
LIMIT 0, 30
UNION ALL
SELECT p.pcol1, p.pcol_2, p.p3
FROM pandom p
WHERE p.userid = '1'
LIMIT 0, 30
UNION ALL
SELECT l.l1, l.l2, l.l3
FROM landom l
WHERE l.userid = '1'
LIMIT 0, 30
The fields don't have to be named the same, but the same types need to line up in position 1, 2 and 3.
The way the limits work is:
it will attempt to get 30 from random.
If it has 30 already, it won't even look at the other 2 tables
if it has less than 30 from random, it will try to fill up to 30 from pandom and only finally landom
SELECT t1.*, t2.*, t3.*
FROM `random` as t1, `pandom` as t2, `landom` as t3
WHERE t1.`userid`='1' AND t2.`userid`='1' AND t3.`userid`='1'
SELECT * FROM `random`
JOIN `pandom` USING (`userid`)
JOIN `landom` USING (`userid`)
WHERE `userid`='1'