MS Access Pass Through Query find duplicates using multiple tables - sql

I'm trying to find all coverage_set_id with more than one benefit_id attached summary_attribute (value=2004687).
The query seems to be working fine without the GROUP BY & HAVING parts, but once I add those lines in (for the COUNT) my results are incorrect. Just trying to get duplicate coverage_set_id.
Pass-Through query via OBDC database:
SELECT DISTINCT
b.coverage_set_id,
COUNT (b.coverage_set_id) AS "COUNT"
FROM
coverage_set_detail_view a
JOIN contracts_by_sub_group_view b ON b.coverage_set_id = a.coverage_set_id
JOIN request c ON c.request_id = b.request_id
WHERE
b.valid_from_date BETWEEN to_date('10/01/2010','mm/dd/yyyy')
AND to_date('12/01/2010','mm/dd/yyyy')
AND c.request_status = 1463
AND summary_attribute = 2004687
AND benefit_id <> 1092333
GROUP BY
b.coverage_set_id
HAVING
COUNT (b.coverage_set_id) > 1
My results look like this:
-----------------------
COVERAGE_SET_ID | COUNT
-----------------------
4193706 | 8
4197052 | 8
4193926 | 112
4197078 | 96
4174168 | 8
I'm expecting all the COUNTs to be 2.
::EDIT::
Solution:
SELECT
c.coverage_set_id AS "COVERAGE SET ID",
c1.description AS "Summary Attribute",
count(d.benefit_id) AS "COUNT"
FROM (
SELECT DISTINCT coverage_set_id
FROM contracts_by_sub_group_view
WHERE
valid_from_date BETWEEN '01-OCT-2010' AND '01-DEC-2010'
AND request_id IN (
SELECT request_id
FROM request
WHERE request_status = 1463)
) a
JOIN coverage_set_master e ON e.coverage_set_id = a.coverage_set_id
JOIN coverage_set_detail c ON c.coverage_set_id = a.coverage_set_id
JOIN benefit_summary d ON d.benefit_id = c.benefit_id
AND d.coverage_type = e.coverage_type
JOIN codes c1 ON c1.code_id = d.summary_attribute
WHERE
d.summary_attribute IN (2004687, 2004688)
AND summary_structure = 1000217
GROUP BY c.coverage_set_id, c1.description
HAVING COUNT(d.benefit_id) > 1
ORDER BY c.coverage_set_id, c1.description
And these were the results:
COVERAGE SET ID | SUMMARY ATTRIBUTE | COUNT
-------------------------------------------------
4174168 | INPATIENT | 2
4174172 | INPATIENT | 2
4191828 | INPATIENT | 2
4191832 | INPATIENT | 2
4191833 | INPATIENT | 2
4191834 | INPATIENT | 2
4191838 | INPATIENT | 2
4191842 | INPATIENT | 2
4191843 | INPATIENT | 2
4191843 | OUTPATIENT | 2
4191844 | INPATIENT | 2
4191844 | OUTPATIENT | 2

The coverage_set_id in both the HAVING and count part of the SELECT should be benefit_id.
Since benefit_id is also in table a you can do the following
SELECT
a.coverage_set_id,
COUNT (a.benefit_id) AS "COUNT"
FROM
coverage_set_detail_view a
WHERE
a.coverage_set_id in (
SELECT b.coverage_set_id
FROM contracts_by_sub_group_view b
WHERE b.valid_from_date BETWEEN to_date('10/01/2010','mm/dd/yyyy') AND to_date('12/01/2010','mm/dd/yyyy'))
AND a.coverage_set_id in (
SELECT b2.coverage_set_id
FROM contracts_by_sub_group_view b2
INNER JOIN request c on c.request_id=b2.request_id
WHERE c.request_status = 1463)
AND ?.summary_attribute = 2004687
AND a.benefit_id <> 1092333
GROUP BY
a.coverage_set_id
HAVING
COUNT (a.benefit_id) > 1
This removes the JOIN magnification that was occurring on the FROM since those tables are not needed to pull coverage_set_id and benefit_id. The only remaining need for the other 2 tables is to filter out data based on criteria, which is in the WHERE clause.
I'm not sure what table summary_attribute lives in but it would follow a similar pattern to valid_from_date, request_status, or benefit_id.

Related

Select and count in the same query on two tables

I've got these two tables:
___Subscriptions
|--------|--------------------|--------------|
| SUB_Id | SUB_HotelId | SUB_PlanName |
|--------|--------------------|--------------|
| 1 | cus_AjGG401e9a840D | Free |
|--------|--------------------|--------------|
___Rooms
|--------|-------------------|
| ROO_Id | ROO_HotelId |
|--------|-------------------|
| 1 |cus_AjGG401e9a840D |
| 2 |cus_AjGG401e9a840D |
| 3 |cus_AjGG401e9a840D |
| 4 |cus_AjGG401e9a840D |
|--------|-------------------|
I'd like to select the SUB_PlanName and count the rooms with the same HotelId.
So I tried:
SELECT COUNT(*) as 'ROO_Count', SUB_PlanName
FROM ___Rooms
JOIN ___Subscriptions
ON ___Subscriptions.SUB_HotelId = ___Rooms.ROO_HotelId
WHERE ROO_HotelId = 'cus_AjGG401e9a840D'
and
SELECT
SUB_PlanName,
(
SELECT Count(ROO_Id)
FROM ___Rooms
Where ___Rooms.ROO_HotelId = ___Subscriptions.SUB_HotelId
) as ROO_Count
FROM ___Subscriptions
WHERE SUB_HotelId = 'cus_AjGG401e9a840D'
But I get empty datas.
Could you please help ?
Thanks.
You need to use GROUP BY whenever you do some aggregation(here COUNT()). Below query will give you the number of ROO_ID only for the SUB_HotelId = 'cus_AjGG401e9a840D' because you have this condition in WHERE. If you want the COUNTs for all Hotel_IDs then you can simply remove the WHERE filter from this query.
SELECT s.SUB_PlanName, COUNT(*) as 'ROO_Count'
FROM ___Rooms r
JOIN ___Subscriptions s
ON s.SUB_HotelId = r.ROO_HotelId
WHERE r.ROO_HotelId = 'cus_AjGG401e9a840D'
GROUP BY s.SUB_PlanName;
To be safe, you can also use COUNT(DISTINCT r.ROO_Id) if you don't want to double count a repeating ROO_Id. But your table structures seem to have unique(non-repeating) ROO_Ids so using a COUNT(*) should work as well.

Calculating Win-rates

I have to calculate win-rates of the players from a table (bots_match_history) which has data in the format:
id | username | sub_level_id | bot_type | match_result | system_win_balance | created_at | analyzed | stakes
------+-----------------+--------------+----------+--------------+--------------------+----------------------------+----------+--------
5487 | ashishish | 5 | hard | l | -831 | 2017-11-29 06:26:13.288267 | f | 18
5486 | dilip.kumar | 3 | hard | l | -821 | 2017-11-29 06:25:09.106075 | f | 50
5485 | abhinav.garg | 5 | hard | w | -791 | 2017-11-29 06:24:07.589281 | f | 18
I need to use only those entries which haven't been analyzed yet (analyzed=false) and which has more than 3 entries for a particular level.
This is the query that I had written, somehow for some entries it is returning a win-rate of > 100%.
WITH total AS (
SELECT COUNT(b.match_result) AS total_matches, b.bot_type, sl.level_id, b.stakes
FROM bots_match_history b
JOIN sub_levels sl ON b.sub_level_id = sl.id
WHERE b.analyzed=FALSE
GROUP BY b.bot_type, sl.level_id, b.stakes
HAVING COUNT(b.match_result) >=3
)
SELECT total.bot_type, total.level_id, total.stakes, round(cast(((
SELECT COUNT(b2.*)
FROM bots_match_history b2
JOIN sub_levels sl2 ON b2.sub_level_id = sl2.id
WHERE b2.match_result='w' AND b2.analyzed=FALSE
AND b2.bot_type = total.bot_type AND sl2.level_id = total.level_id
)::FLOAT * 100.0 / total.total_matches) AS NUMERIC), 2)::FLOAT AS win_percentage
FROM total, bots_match_history b3
JOIN sub_levels sl3 ON sl3.id = b3.sub_level_id
WHERE b3.bot_type = total.bot_type AND sl3.level_id=total.level_id
GROUP BY total.bot_type, total.level_id, total.stakes, total.total_matches;
What is wrong in this query that it is returning a win-rate of more than 100%?
You are grouping your total by stakes, but do not JOIN by it later, so one row from total (limited to certain stakes) can be matched with more rows than calculating total did (depends on how many of those are won).
I have made fiddle, excluding sub_levels table and substituting it with sub_level_id instead for grouping and joins: http://dbfiddle.uk/?rdbms=postgres_9.6&fiddle=b3558b9d3cddc63a47d3f29a4a6c08f6
Fixed SQL should look like this:
WITH total AS (
SELECT COUNT(b.match_result) AS total_matches, b.bot_type, sl.level_id, b.stakes
FROM bots_match_history b
JOIN sub_levels sl ON b.sub_level_id = sl.id
WHERE b.analyzed=FALSE
GROUP BY b.bot_type, sl.level_id, b.stakes
HAVING COUNT(b.match_result) >=3
)
SELECT total.bot_type, total.level_id, total.stakes, round(cast(((
SELECT COUNT(b2.*)
FROM bots_match_history b2
JOIN sub_levels sl2 ON b2.sub_level_id = sl2.id
WHERE b2.match_result='w' AND b2.analyzed=FALSE
AND b2.bot_type = total.bot_type AND sl2.level_id = total.level_id
AND b2.stakes = total.stakes
)::FLOAT * 100.0 / total.total_matches) AS NUMERIC), 2)::FLOAT AS win_percentage
FROM total, bots_match_history b3
JOIN sub_levels sl3 ON sl3.id = b3.sub_level_id
WHERE b3.bot_type = total.bot_type AND sl3.level_id=total.level_id
AND b3.stakes=total.stakes
GROUP BY total.bot_type, total.level_id, total.stakes, total.total_matches;
Also, you might get (IMO) incorrect results if you limit yourself to analyzed rows only. I saw 40% winrate for one set of parameters, then checked actual rows and there was actually 3 won and 3 lost games with left me puzzled for a while only to notice that one of those won games was "analyzed" already.

SQL union / join / intersect multiple select statements

I have two select statements. One gets a list (if any) of logged voltage data in the past 60 seconds and related chamber names, and one gets a list (if any) of logged arc event data in the past 5 minutes. I am trying to append the arc count data as new columns to the voltage data table. I cannot figure out how to do this.
Note that, there may or may not be arc count rows, for a given chamber name that is in the voltage data table. If there are no rows, I want to set the arc count column value to zero.
Any ideas on how to accomplish this?
Voltage Data:
SELECT DISTINCT dbo.CoatingChambers.Name,
AVG(dbo.CoatingGridVoltage_Data.ChanA_DCVolts) AS ChanADC,
AVG(dbo.CoatingGridVoltage_Data.ChanB_DCVolts) AS ChanBDC,
AVG(dbo.CoatingGridVoltage_Data.ChanA_RFVolts) AS ChanARF,
AVG(dbo.CoatingGridVoltage_Data.ChanB_RFVolts) AS ChanBRF FROM
dbo.CoatingGridVoltage_Data LEFT OUTER JOIN dbo.CoatingChambers ON
dbo.CoatingGridVoltage_Data.CoatingChambersID =
dbo.CoatingChambers.CoatingChambersID WHERE
(dbo.CoatingGridVoltage_Data.DT > DATEADD(second, - 60,
SYSUTCDATETIME())) GROUP BY dbo.CoatingChambers.Name
Returns
Name | ChanADC | ChanBDC | ChanARF | ChanBRF
-----+-------------------+--------------------+---------------------+------------------
OX2 | 2.9099999666214 | -0.485000004371007 | 0.344801843166351 | 0.49748428662618
S2 | 0.100000001490116 | -0.800000016887983 | 0.00690172302226226 | 0.700591623783112
S3 | 4.25666658083598 | 0.5 | 0.96554297208786 | 0.134956782062848
Arc count table:
SELECT CoatingChambers.Name,
SUM(ArcCount) as ArcCount
FROM CoatingChambers
LEFT JOIN CoatingArc_Data
ON dbo.[CoatingArc_Data].CoatingChambersID = dbo.CoatingChambers.CoatingChambersID
where EventDT > DATEADD(mi,-5, GETDATE())
Group by Name
Returns
Name | ArcCount
-----+---------
L1 | 283
L4 | 0
L6 | 1
S2 | 55
To be clear, I want this table (with added arc count column), given the two tables above:
Name | ChanADC | ChanBDC | ChanARF | ChanBRF | ArcCount
-----+-------------------+--------------------+---------------------+-------------------+---------
OX2 | 2.9099999666214 | -0.485000004371007 | 0.344801843166351 | 0.49748428662618 | 0
S2 | 0.100000001490116 | -0.800000016887983 | 0.00690172302226226 | 0.700591623783112 | 55
S3 | 4.25666658083598 | 0.5 | 0.96554297208786 | 0.134956782062848 | 0
You can treat the select statements as virtual tables and just join them together:
select
x.Name,
x.ChanADC,
x.ChanBDC,
x.ChanARF,
x.ChanBRF,
isnull( y.ArcCount, 0 ) ArcCount
from
(
select distinct
cc.Name,
AVG(cgv.ChanA_DCVolts) AS ChanADC,
AVG(cgv.ChanB_DCVolts) AS ChanBDC,
AVG(cgv.ChanA_RFVolts) AS ChanARF,
AVG(cgv.ChanB_RFVolts) AS ChanBRF
from
dbo.CoatingGridVoltage_Data cgv
left outer join
dbo.CoatingChambers cc
on
cgv.CoatingChambersID = cc.CoatingChambersID
where
cgv.DT > dateadd(second, - 60, sysutcdatetime())
group by
cc.Name
) as x
left outer join
(
select
cc.Name,
sum(ac.ArcCount) as ArcCount
from
dbo.CoatingChambers cc
left outer join
dbo.CoatingArc_Data ac
on
ac.CoatingChambersID = cc.CoatingChambersID
where
EventDT > dateadd(mi,-5, getdate())
group by
Name
) as y
on
x.Name = y.Name
Also, it's worthwhile to simplify your names with aliases and format the queries for readability...which I shamelessly took a stab at.

SQL to search with a subsets of records

Is there any way to select all subsets from table A where corresponding subsets exist in table B? Each subset in table A must have at least all the entries that corresponding subset in table B.
In this link it's called "Division with a Remainder" but my problem is more complex because I've got many to many relation.
Table A
UserName text | File text | AccessLevel int
Table B
AppName text | File text | AccessLevel int
Sample data
Table A
User1 | aaa.txt | 1
User1 | bbb.txt | 3
User1 | ccc.txt | 1
User2 | aaa.txt | 3
Table B
Appl1 | aaa.txt | 1
Appl1 | bbb.txt | 1
Appl2 | aaa.txt | 1
Appl3 | bbb.txt | 5
Appl4 | aaa.txt | 1
Appl4 | bbb.txt | 1
Appl4 | ccc.txt | 1
Appl4 | ddd.txt | 1
Expected results:
User1 | Appl1
User1 | Appl2
User2 | Appl2
User1 has "complete" access to applications Appl1 and Appl2 because he has necessary access to ALL files used by these applications. He doesn't have access to application Appl3 because access level is not high enough. He doesn't have access to application Appl4 because he doesn't have access to file ddd.txt.
Basically I need to compare subsets of records and return all cases where subset in table B is equal or greater than subset in table A. Is there any way to do it in SQL?
Any help appreciated.
One method is a self-join and then comparing the number of matching records to the number needed for the application.
Assuming no duplicates:
select a.username, b.appname
from a join
(select b.*, count(*) over (partition by b.appname) as cnt
from b
) b
on a.filetext = b.filetext and
a.accesslevel >= b.accesslevel
group by a.username, b.appname, b.cnt
having count(*) = b.cnt
SELECT distinct A1.USERNAME, B1.APPLICATION
FROM TABLEUSER A1
INNER JOIN TABLEAPPLI B1 on B1.filename=A1.filename and B1.accesslevel<=A1.accesslevel
where not exists
(
select *
from TABLEUSER A2 INNER JOIN TABLEAPPLI B2 on B2.filename<>A2.filename or B2.accesslevel>A2.accesslevel
where (A1.USERNAME, B1.APPLICATIONAME) = (A2.USERNAME, B2.APPLICATIONAME)
)

join on three tables? Error in phpMyAdmin

I'm trying to use a join on three tables query I found in another post (post #5 here). When I try to use this in the SQL tab of one of my tables in phpMyAdmin, it gives me an error:
#1066 - Not unique table/alias: 'm'
The exact query I'm trying to use is:
select r.*,m.SkuAbbr, v.VoucherNbr from arrc_RedeemActivity r, arrc_Merchant m, arrc_Voucher v
LEFT OUTER JOIN arrc_Merchant m ON (r.MerchantID = m.MerchantID)
LEFT OUTER JOIN arrc_Voucher v ON (r.VoucherID = v.VoucherID)
I'm not entirely certain it will do what I need it to do or that I'm using the right kind of join (my grasp of SQL is pretty limited at this point), but I was hoping to at least see what it produced.
(What I'm trying to do, if anyone cares to assist, is get all columns from arrc_RedeemActivity, plus SkuAbbr from arrc_Merchant where the merchant IDs match in those two tables, plus VoucherNbr from arrc_Voucher where VoucherIDs match in those two tables.)
Edited to add table samples
Table arrc_RedeemActivity
RedeemID | VoucherID | MerchantID | RedeemAmt
----------------------------------------------
1 | 2 | 3 | 25
2 | 6 | 5 | 50
Table arrc_Merchant
MerchantID | SkuAbbr
---------------------
3 | abc
5 | def
Table arrc_Voucher
VoucherID | VoucherNbr
-----------------------
2 | 12345
6 | 23456
So ideally, what I'd like to get back would be:
RedeemID | VoucherID | MerchantID | RedeemAmt | SkuAbbr | VoucherNbr
-----------------------------------------------------------------------
1 | 2 | 3 | 25 | abc | 12345
2 | 2 | 5 | 50 | def | 23456
The problem was you had duplicate table references - which would work, except for that this included table aliasing.
If you want to only see rows where there are supporting records in both tables, use:
SELECT r.*,
m.SkuAbbr,
v.VoucherNbr
FROM arrc_RedeemActivity r
JOIN arrc_Merchant m ON m.merchantid = r.merchantid
JOIN arrc_Voucher v ON v.voucherid = r.voucherid
This will show NULL for the m and v references that don't have a match based on the JOIN criteria:
SELECT r.*,
m.SkuAbbr,
v.VoucherNbr
FROM arrc_RedeemActivity r
LEFT JOIN arrc_Merchant m ON m.merchantid = r.merchantid
LEFT JOIN arrc_Voucher v ON v.voucherid = r.voucherid