Can i merge the result of two queries? - sql

I'm trying to retrieve some statistics from my database, to be concrete i'm look to show how many todo's is completed vs the total of a checklist.
The structure is as follows
A category has many Cards, has many checklists, has many assessments.
I can get the amount of assessments or completed assessments with the following query.
SELECT count(a.id) AS completed_count, a.checklist_id, ca.category_id
FROM assessments a
JOIN checklists ch ON ch.id = a.checklist_id
JOIN cards ca ON ca.id = ch.card_id
WHERE a.complete
GROUP BY a.checklist_id, ca.category_id;
This will give me something like this.
completed_count | checklist_id | category_id
-----------------+--------------+-------------
2 | 3 | 2
1 | 2 | 2
2 | 5 | 3
I could then do a query, to get the total amount, by removing the WHERE a.complete, and write some code that matches the two results.
But what i really want, is a result like this.
completed_amount | total_amount | checklist_id | category_id
------------------+--------------+--------------+-------------
2 | 2 | 3 | 2
1 | 1 | 2 | 2
2 | 2 | 5 | 3
I just can't wrap my head around, how i can achieve that.

I think conditional aggregation does what you want:
SELECT sum( (a.complete)::int ) AS completed_count,
count(*) as total_count
a.checklist_id, ca.category_id
FROM assessments a JOIN
checklists ch
ON ch.id = a.checklist_id JOIN
cards ca
ON ca.id = ch.card_id
GROUP BY a.checklist_id, ca.category_id;

Related

Include zero counts when grouping by multiple columns and setting filters

I have a table (tbl) containing category (2 categories), impact (3 impacts), company name and date for example:
category | impact | company | date | number
---------+----------+---------+-----------|
Animal | Critical | A | 12/31/1999|1
Book | Critical | B | 12/31/2000|2
Animal | Minor | C | 12/31/2001|3
Book | Minor | D | 12/31/2002|4
Animal | Medium | E | 1/1/2003 |5
I want to get the count of records for each category and impact and be able to add rows with zero count and also be able to filter by company and date.
In the example result set below, the count result is 1 for category = Animal and company = A. The rest is 0 records and only the Critical and Medium impacts appear
category | impact | count
---------+----------+-------
Animal | Critical | 1
Animal | Medium | 0
I've looked at the responses to similar questions by using joins however, adding a WHERE clause doesn't include the zero records.
I also tried doing outer joins but it doesn't produce desired output. For example
select a.impact, b.category, ISNULL(count(b.impact), 0) from tbl a
left outer join tbl b
on b.number = a.number
and (a.category = 'Animal' and a.company in ('A'))
group by a.impact, b.category
produces
impact | category | count
---------+------------+--------
Medium | NULL | 0
Medium | Animal | 1
Critical | NULL | 0
Minor | NULL | 0
but the desired output should be
category | impact | count
---------+----------+-------
Animal | Critical | 1
Animal | Medium | 0
Animal | Minor | 0
Any help will be appreciated. Answers to associated questions don't have filtering so I will appreciate if someone can help with a query to produce desired output.
You need a master table with all the possible combinations of Categories and Impacts for this. Then Left join your table with the master and do the aggregation. Something like below
;WITH CAT
AS
(
SELECT
category
FROM Tbl
GROUP BY category
),
IMP
AS
(
SELECT
Impact
FROM Tbl
GROUP BY Impact
),MST
AS
(
SELECT
*
FROM CAT
CROSS JOIN IMP
)
SELECT
MST.category,
MST.Impact,
COUNT(T.Number)
FROM MST
LEFT JOIN Tbl T
ON MST.category = T.category
AND MST.Impact = T.Impact
AND T.Company = 'A'
WHERE MST.Category = 'Animal' GROUP BY MST.category,
MST.Impact

Find SQL table rows where there are multiple different values

I want to be able to filter out groups where the values aren't the same. When doing the query:
SELECT
category.id as category_id,
object.id as object_id,
object.value as value
FROM
category,
object
WHERE
category.id = object.category
We get the following results:
category_id | object_id | value
-------------+-----------+-------
1 | 1 | 1
1 | 2 | 2
1 | 3 | 2
2 | 4 | 3
2 | 5 | 2
3 | 6 | 1
3 | 7 | 1
The goal: Update the query so that it yields:
category_id
-------------
1
2
In other words, find the categories where the values are different from the others in that same category.
I have tried many different methods of joining, grouping and so on, to no avail.
I know it can be done with multiple queries and then filter with a little bit of logic, but this is not the goal.
You can use aggregation:
SELECT o.category as category_id
FROM object o
GROUP BY o.category
HAVING MIN(o.value) <> MAX(o.value);
You have left the FROM clause out of your query. But as written, you don't need a JOIN at all. The object table is sufficient -- because you are only fetching the category id.

SQL select only highest date

For a project I want to generate a price list.
I want to get only the latest prices from each supplier for each article.
There are just those two tables.
Table articles
ARTNR | TXT | ACTIVE | SUPPLIER
------------------------------------------
10 | APPLE | Y | 10
20 | ORANGE | Y | 10
30 | KEYBOARD | N | 20
40 | ORANGE | Y | 20
50 | BANANA | Y | 10
60 | CHERRY | Y | 10
Table prices
ARTNR | PRCGRP | PRCDAT | PRICE
--------------------------------------
10 | 10 | 01-Aug-10 | 2.1
10 | 10 | 05-Aug-11 | 2.2
10 | 10 | 21-Aug-12 | 2.5
20 | 0 | 01-Aug-10 | 2.1
20 | 10 | 09-Aug-12 | 2.3
10 | 10 | 14-Aug-13 | 2.7
This is what I have so far:
SELECT
ARTICLES.[ARTNR], ARTICLES.[TXT], ARTICLES.[ACTIVE], ARTICLES.[SUPPLIER], PRICES.PRCGRP, PRICES.PRCDAT, PRICES.PRICE
FROM
ARTICLES INNER JOIN PRICES ON ARTICLES.ARTNR = PRICES.ARTNR
WHERE
(
(ARTICLES.[ACTIVE]="Y") AND
(ARTICLES.[SUPPLIER]=10) AND
(PRICES.PRCGRP=0) AND
(PRICES.PRCDAT=(SELECT MAX(PRCDAT) FROM PRICES as art WHERE art.ARTNR = PRICES.artnr) )
)
ORDER BY ARTICLES.ARTNR
;
It is okay to choose just one supplier each time, but I want the max price.
The problem is:
Lots of articles do not show up with the query above,
but I cannot figure out what is wrong.
I can see that they should be in the resultset when I leave out the subselect on max prcdat.
What is wrong?
Your subquery to get the latest price does not take the other conditions into account, that is when you're getting the latest price, you may get a price in another price group or that is not active. When you join that against the filtered list that has no inactive prices and only prices in a single price group, you get no hits that exist in both.
Either you need to duplicate or - better - move your conditions inside the subquery to get the best price under the conditions. I can't test against access, but something like this should be possible if the SQL is not too limited;
SELECT a.artnr, a.txt, a.active, a.supplier, p.prcgrp, p.prcdat, p.price
FROM articles a INNER JOIN prices p ON a.ARTNR = p.ARTNR
JOIN (
SELECT a.artnr, MAX(p.prcdat) prcdat
FROM articles a JOIN prices p ON a.artnr = p.artnr
WHERE a.active='Y' AND a.supplier=10 AND p.prcgrp=10
GROUP BY a.artnr) z
ON a.artnr = z.artnr AND p.prcdat = z.prcdat
ORDER BY a.ARTNR
If the SQL support in access won't allow a join with a subquery, you can just move the conditions inside your existing subquery, something like;
SELECT a.artnr, a.txt, a.active, a.supplier, p.prcgrp, p.prcdat, p.price
FROM articles a INNER JOIN prices p ON a.ARTNR = p.ARTNR
WHERE p.prcdat = (
SELECT MAX(p2.prcdat)
FROM articles a2 JOIN prices p2 ON a2.artnr = p2.artnr
WHERE a.artnr = a2.artnr AND a2.active='Y' AND a2.supplier=10 AND p2.prcgrp=10
)
ORDER BY a.ARTNR;
Note that due to limitations in identifying a unique price (no primary key in prices), the queries may give duplicates if several prices for the same article have the same prcdat. If that's a problem, you'll probably need to duplicate your conditions outside the subquery too.

SQL Order By Within A Count(Distinct)

I have the following tables:
filetype1
F1_ID | F1_ORDR | FILENAME
1 | 1 | file1.txt
2 | 2 | file2.txt
3 | 3 | file3.txt
4 | 2 | file4.txt
5 | 4 | file5.txt
filetype2
F2_ID | F2_ORDR | FILENAME
1 | 1 | file6.txt
2 | 2 | file7.txt
3 | 4 | file8.txt
ordr
OR_ID | OR_VENDOR
1 | 1
2 | 1
3 | 1
4 | 1
vendor
VE_ID | VE_NAME
1 | Company1
My goal is to have a list of vendors and a count of the number of orders where a file is connected for each type. For example, the end result of this data should be:
VENDOR | OR_CT | F1_CT | F2_CT
Company1 | 4 | 4 | 3
Because at least 1 type1 file was attached to 4 distinct orders and at least 1 type2 file was attached to 3 distinct orders. Currently my SQL code looks like this:
SELECT vendor.ve_id, vendor.ve_name,
(SELECT COUNT(or_id)
FROM ordr
WHERE ordr.or_vendor = vendor.ve_id) as OR_COUNT,
(SELECT COUNT(DISTINCT f1_order)
FROM filetype1 INNER JOIN ordr ON filetype1.f1_ordr = ordr.or_id
WHERE ordr.or_vendor = vendor.ve_id) as F1_CT,
(SELECT COUNT(DISTINCT f2_ordr)
FROM filetype2 INNER JOIN ordr ON filetype2.f2_ordr = ordr.or_id
WHERE ordr.or_vendor = vendor.ve_id) as F2_CT
FROM vendor
ORDER BY vendor.ve_name
Unfortunately this yields the following results:
VENDOR | OR_COUNT | F1_COUNT | F2_COUNT
Company1 | 4 | 5 | 3
My only guess is that because I'm using COUNT(DISTINCT) the COUNT is automatically assuming the DISTINCT is ordering by F1_ID instead of by F1_ORDR
If anyone can assist me on how to tell the COUNT(DISTINCT) to order by F1_ORDR that would be most helpful. I have searched the vast internet for a solution but its hard to explain what I want to a search engine, forums, etc. My database uses Microsoft SQL Server. My knowledge of database management is almost completely self taught, so I'm just glad I made it this far on my own. My expertise is in web design. Thank you for your time.
Your SQL yields the result you want for me.
Two pieces of advice
Order is a bad name for a table - it conflicts with a reserved word, and will cause you no end of hassle
You should join your tables like so
FROM filetype1
inner join [order]
on filetype1.f1_order = or_id
rather than using a where clause
Perhaps try this instead
select
vendor.VE_ID, vendor.VE_NAME,
count(distinct or_id),
count(distinct f1_order),
count(distinct f2_order)
from
vendor
left join [order]
on vendor.VE_ID = [order].OR_VENDOR
inner join filetype1
on [order].OR_ID = filetype1.F1_ORDER
left join filetype2
on [order].OR_ID = filetype2.F2_ORDER
group by
vendor.VE_ID, vendor.VE_NAME
Try this:
SELECT
vdr.VE_NAME
,COUNT(DISTINCT OR_ID) AS OR_ID
,COUNT(DISTINCT ft1.F1_ORDER) AS FT1_COUNT
,COUNT(DISTINCT ft2.F2_ORDER) AS FT2_COUNT
FROM
vendor vdr
LEFT OUTER JOIN [order] odr
ON vdr.VE_ID = odr.OR_VENDOR
INNER JOIN filetype1 ft1
ON odr.OR_ID = ft1.F1_ORDER
LEFT OUTER JOIN filetype2 ft2
ON odr.OR_ID = ft2.F2_ORDER
GROUP BY
vdr.VE_ID
,vdr.VE_NAME
I will propose you this:
Merge filetype1 and filetype2 tables in one table(filetype) and add another field named - f_type(for instance) of type INT or TINTYINT to store the filetype (1 or 2). This has the benefits of painlessly adding another filetype later
Now the query will look something like this:
SELECT
vendor.ve_name,
count(DISTINCT filetype.f_order),
filetype.f_type
FROM
filetype
INNER JOIN `order`
ON filetype.f_order = `order`.or_id
INNER JOIN vendor
ON `order`.or_vendor = vendor.ve_id
GROUP BY vendor.ve_id,filetype.f_type
This will give the count of orders for filetype.
For the total orders just add another query:
SELECT count(*) FROM `order`

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