SQL - Combination of Distinct & Count in a table - sql

Need a simple query to summarize result from a table where 3 columns are present:
Order ID, Category & Brand.
The summary should contain order ID, distinct count of category and distinct count of brand belonging to the order ID.
Sample Data:
orderno product brand
1 A Z
1 A X
1 B Y
2 C X
2 B X
3 C X
3 B Y
Expected Result:
orderno product brand
1 2 3
2 2 1
3 2 2
Sample Data & Summary

Use DISTINCT of COUNT to get your expected result:
select orderno, count(distinct product) as product, count(distinct brand) as brand
from testtable
group by orderno
Sample execution with the given data
declare #test1 table (orderno int, product varchar(2), brand varchar(2))
insert into #test1 (orderno, product, brand) values
(1, 'A', 'Z'),
(1, 'A', 'X'),
(1, 'B', 'Y'),
(2, 'C', 'X'),
(2, 'B', 'X'),
(3, 'C', 'X'),
(3, 'B', 'Y');
select orderno, count(distinct product) as product, count(distinct brand) as brand
from #test1
group by orderno
Result:
orderno product brand
1 2 3
2 2 1
3 2 2

Try this,.
select orderno,count(DISTINCT product) product,COUNT(DISTINCT brand) brand
from data
GROUP by orderno
The output.,
orderno product brand
----------- ----------- -----------
1 2 3
2 2 1
3 2 2

Related

SQL sort and last record

this is my first post so pardon me if my question is not in it's appropriate places or tittle
I have a table like this
ID DATE Cat VALUE
-------------------------
1 07/07/2018 A 100
2 07/07/2018 A 200
3 07/07/2018 B 300
4 07/07/2018 B 400
5 07/07/2018 C 500
6 07/07/2018 C 600
7 08/07/2018 A 700
8 08/07/2018 A 800
9 08/07/2018 B 900
10 08/07/2018 B 110
11 08/07/2018 C 120
I would like to return
distinct category, sum of value, last record of the category
something like this
Cat sumValue lastrecord
--------------------------
A 1800 800
B 1710 110
C 1220 120
is it possible to do it in a single query
thanks
I am able to find the SUM
SELECT cat, SUM(value) FROM table GROUP BY cat;
and
find the last ID (autonumber key) using MAX
SELECT MAX(ID), cat FROM table GROUP BY cat;
but i just can't get the value for the last record
SQLFiddle
SELECT
t.cat,
SUM(t.value) as sumValue,
(
SELECT
t3.value
FROM
`table` t3
WHERE
t3.id = MAX(t2.id)
) as lastrecord
FROM
`table` t
JOIN
`table` t2 ON t.id = t2.id
GROUP BY
cat
EDIT shorter Version:
SELECT
t.cat,
SUM(t.value) as sumValue,
(SELECT value FROM `table` t2 WHERE t2.id = MAX(t.id)) lastValue
FROM
`table` t
GROUP BY
t.cat
This should do it
declare #t table (id int, cat char, value int);
insert into #t values
(1, 'A', 100),
(2, 'A', 200),
(3, 'B', 300),
(4, 'B', 400),
(5, 'C', 500),
(6, 'C', 600),
(7, 'A', 700),
(8, 'A', 800),
(9, 'B', 900),
(10, 'B', 110),
(11, 'C', 120);
select cat, value, sum
from
( select *
, sum(value) over (partition by cat) as sum
, ROW_NUMBER() over (partition by cat order by id desc) as rn
from #t
) tt
where tt.rn = 1
I hope you're looking for something like this,
Please replace the table name with your table name.
SELECT A.id,
A.cat,
A.date,
A.total_value,
A1.value
FROM (SELECT Max(id) AS id,
cat,
Max(date) AS Date,
Sum(value) AS Total_Value
FROM tbl_sof
GROUP BY cat) AS A
INNER JOIN tbl_sof A1
ON A.id = A1.id

I want to output a list of every Case_Number with Code 1 that has a higher UniqueID value than its Code 2 counterpart's UniqueID value

I have a table which looks something like this:
Case_Number | Code | UniqueID
a 1 1372
a 2 1352
a 3 1325
b 1 1642
b 2 1651
b 3 1623
c 1 1743
c 2 1739
c 3 1720
... ... ...
From this database I want to output a list of every Case_Number where the UniqueID value of Code 1 is higher than the UniqueID value of Code 2 (But ignoring the UniqueID value of Code 2, or any other Code x that might be in the table). Meaning that if the UniqueID value of Code 2 is higher than Code 1, which is the case with Case_Number b in the example above, it should not show up in the list.
So, querying the above table would result in this:
Case_Number | Code | UniqueID
a 1 1372
c 1 1743
Hmmm . . . You seem to want:
select t.*
from t
where t.code = 1 and
t.uniqueid > (select max(t2.uniqueid)
from t t2
where t2.case_number = t.case_number and t2.code = 2
);
The max() in the subquery is simply to handle the case where there is more than one matching value.
The query below gives you the expected result
CREATE TABLE CaseTab
(Case_Number VARCHAR(10),
Code INT,
UniqueID INT);
INSERT INTO CaseTab VALUES ('a', 1, 1372);
INSERT INTO CaseTab VALUES ('a', 2, 1352);
INSERT INTO CaseTab VALUES ('a', 3, 1325);
INSERT INTO CaseTab VALUES ('b', 1, 1642);
INSERT INTO CaseTab VALUES ('b', 2, 1651);
INSERT INTO CaseTab VALUES ('b', 3, 1623);
INSERT INTO CaseTab VALUES ('c', 1, 1743);
INSERT INTO CaseTab VALUES ('c', 2, 1739);
INSERT INTO CaseTab VALUES ('c', 3, 1720);
WITH v_code_gt_1 AS
(SELECT Case_Number, MAX(UniqueID) AS UniqueID
FROM CaseTab
WHERE Code > 1
GROUP BY Case_Number)
SELECT c1.Case_Number, c1.UniqueID
FROM CaseTab c1 JOIN
v_code_gt_1 c2
ON (c1.Case_Number = c2.Case_Number)
WHERE c1.UniqueID > c2.UniqueID
AND c1.Code = 1;
Basically the query gets the max UniqueID for all cases where code is greater than 1 and compares against the Unique ID for Code 1.
You haven't stated whether there can be cases with code = 1, but no other codes. If so, use LEFT JOIN as below.
WITH v_code_gt_1 AS
(SELECT Case_Number, MAX(UniqueID) AS UniqueID
FROM CaseTab
WHERE Code > 1
GROUP BY Case_Number)
SELECT c1.Case_Number, c1.UniqueID
FROM CaseTab c1 LEFT JOIN
v_code_gt_1 c2
ON (c1.Case_Number = c2.Case_Number)
WHERE c1.UniqueID > ISNULL(c2.UniqueID, 0)
AND c1.Code = 1;

ADD Specific values in SQL Column determined by other Column

I have a Database that determines different values based on a label.
Where the label determines whether it's an exempted value or not.
For instance, 2 = non exempted and 3 = exempted. If I run a query my results look something like this
|Name |ExemptionStatus |Total Value|
|X |2 |100 |
|X |3 |200 |
My Query is
SELECT NAME, EXEMPTIONSTATUS
SUM(TOTAL_VALUE) AS 'TOTAL VALUE'
FROM ORDER_ACCOUNT JOIN ACCOUNT_INVOICE
WHERE ORDER_ACCOUNT.DATE BETWEEN 'M/D/YEAR' AND 'M/D/YEAR'
GROUP BY NAME, EXEMPTIONSTATUS
ORDER BY NAME ASC
How can I get my query to create a new column for the values, for example:
|Name |NON EXEMPT VALUE|EXEMPT VALUE|
|X |100 |200 |
I just don't know how how I would sort it whether it's in my Where clause or not.
Use a CASE statement within a SUM to only total NON EXEMPT, then EXEMPT, and select them as separate columns. Similar to the following (might need to add TOTAL_VALUE to the GROUP BY, or remove EXEMPTIONSTATUS)
SELECT
NAME
,SUM(CASE WHEN EXEMPTIONSTATUS = 2 THEN TOTAL_VALUE ELSE 0 END) AS 'NON EXEMPT VALUE'
,SUM(CASE WHEN EXEMPTIONSTATUS = 3 THEN TOTAL_VALUE ELSE 0 END) AS 'EXEMPT VALUE'
FROM ORDER_ACCOUNT JOIN ACCOUNT_INVOICE
WHERE ORDER_ACCOUNT.DATE BETWEEN 'M/D/YEAR' AND 'M/D/YEAR'
GROUP BY NAME, EXEMPTIONSTATUS
ORDER BY NAME ASC
EDIT: New code below adds new columns to your existing table. you will need to replace the #Test with your tables, but I believe this will get you what you're looking for.
SELECT
NAME,
EXEMPTIONSTATUS
,[TOTAL_VALUE]
,(SELECT SUM(CASE WHEN EXEMPTIONSTATUS = 2 THEN TOTAL_VALUE ELSE 0 END) FROM #Test t WHERE t.NAME = NAME) 'NON EXEMPT VALUE'
,(SELECT SUM(CASE WHEN EXEMPTIONSTATUS = 3 THEN TOTAL_VALUE ELSE 0 END) FROM #Test t WHERE t.NAME = NAME) 'EXEMPT VALUE'
FROM #Test
This gives me the following output
| NAME | EXEMPTIONSTATUS | TOTAL_VALUE | NON EXEMPT VALUE | EXEMPT VALUE |
| X | 2 | 100 | 100 | 200 |
| X | 3 | 200 | 100 | 200 |
Let's say your table structure is like this:
CREATE TABLE tab(ID int, Name nvarchar(20), ExemptionStatus int, TotalValue int);
INSERT INTO tab(ID, Name, ExemptionStatus, TotalValue) values (1, 'X', 2, 100);
INSERT INTO tab(ID, Name, ExemptionStatus, TotalValue) values (2, 'X', 3, 200);
So your data looks like this:
ID Name ExemptionStatus TotalValue
1 X 2 100
2 X 3 200
Then the query you'd use is:
SELECT NotExempted.Name,
NotExempted.NonExemptValue,
Exempted.ExemptValue
FROM (SELECT Name,
CASE
WHEN ExemptionStatus = 2 THEN TotalValue
END
AS 'NonExemptValue'
FROM #tab
) NotExempted
INNER JOIN (SELECT Name,
CASE
WHEN ExemptionStatus = 3 THEN TotalValue
END
AS 'ExemptValue'
FROM #tab
) Exempted ON NotExempted.Name = Exempted.Name
WHERE NotExempted.NonExemptValue IS NOT NULL
AND Exempted.ExemptValue IS NOT NULL
GROUP BY NotExempted.Name,
NotExempted.NonExemptValue,
Exempted.ExemptValue
You result will look like this :
Name NonExemptValue ExemptValue
X 100 200
You can see this here -> http://sqlfiddle.com/#!9/8902d3/2
Now, let's say you have data like this :
CREATE TABLE #tab(ID int, Name nvarchar(20), ExemptionStatus int, TotalValue int)
INSERT INTO #tab(ID, Name, ExemptionStatus, TotalValue) values (1, 'X', 2, 100)
INSERT INTO #tab(ID, Name, ExemptionStatus, TotalValue) values (2, 'X', 3, 200)
INSERT INTO #tab(ID, Name, ExemptionStatus, TotalValue) values (3, 'X', 2, 1000)
INSERT INTO #tab(ID, Name, ExemptionStatus, TotalValue) values (4, 'X', 3, 2000)
INSERT INTO #tab(ID, Name, ExemptionStatus, TotalValue) values (5, 'X', 2, 1045)
INSERT INTO #tab(ID, Name, ExemptionStatus, TotalValue) values (6, 'X', 3, 2045)
INSERT INTO #tab(ID, Name, ExemptionStatus, TotalValue) values (7, 'X', 2, 1034)
INSERT INTO #tab(ID, Name, ExemptionStatus, TotalValue) values (8, 'X', 3, 2023)
INSERT INTO #tab(ID, Name, ExemptionStatus, TotalValue) values (9, 'X', 2, 1023)
INSERT INTO #tab(ID, Name, ExemptionStatus, TotalValue) values (10, 'X', 3, 2076)
which looks like this:
ID Name ExemptionStatus TotalValue
1 X 2 100
2 X 3 200
3 X 2 1000
4 X 3 2000
5 X 2 1045
6 X 3 2045
7 X 2 1034
8 X 3 2023
9 X 2 1023
10 X 3 2076
If you need to sum the total value up, then you can use the following query (which is a slight modification of the query above):
SELECT NotExempted.Name,
NotExempted.NonExemptValue,
Exempted.ExemptValue
FROM (SELECT Name,
CASE
WHEN ExemptionStatus = 2 THEN (SELECT SUM(TotalValue) FROM #tab WHERE ExemptionStatus = 2)
END
AS 'NonExemptValue'
FROM #tab
) NotExempted
INNER JOIN (SELECT Name,
CASE
WHEN ExemptionStatus = 3 THEN (SELECT SUM(TotalValue) FROM #tab WHERE ExemptionStatus = 3)
END
AS 'ExemptValue'
FROM #tab
) Exempted ON NotExempted.Name = Exempted.Name
WHERE NotExempted.NonExemptValue IS NOT NULL
AND Exempted.ExemptValue IS NOT NULL
GROUP BY NotExempted.Name,
NotExempted.NonExemptValue,
Exempted.ExemptValue
Your result will look like this :
Name NonExemptValue ExemptValue
X 4202 8344
You can see this here -> http://sqlfiddle.com/#!9/02c76/3
Hope this helps!!!

How to select multi record depending on some column's condition?

Say there is a SQL Server table which contain 2 columns: ID, Value
The sample data looks like this:
ID value
------------------
1 30
1 30
2 50
2 50
3 50
When I run this query:
select ID, NEWID(), value
from table1
order by ID
The result looks like this:
1 30 E152AD19-9920-4567-87FF-C4822FD9E485
1 30 54F28C58-ABA9-4DFB-9A80-CE9C4C390CBB
2 50 ........
2 50 ........
3 50 4E5A9E26-FEEC-4CC7-9AC5-96747053B6B2
But what I want is : how many record of ID depending on (sum of value /30 )'s result, for example of ID 2, it's value's sum is 50+50=100, and 100/30=3, so ID 2 will display in query result three times
The final result i want is like this:
1 E152AD19-9920-4567-87FF-C4822FD9E485
1 54F28C58-ABA9-4DFB-9A80-CE9C4C390CBB
2 4E5A9E26-FEEC-4CC7-9AC5-96747053B6B2
2 ....
2 ....
3 D861563E-E01A-4198-9E92-7BEB4678E5D1
Please note ID of 2 display three times, wait for your helps, thanks.
How about something like
CREATE TABLE Table1
([ID] int, [value] int)
;
INSERT INTO Table1
([ID], [value])
VALUES
(1, 30),
(1, 30),
(2, 50),
(2, 50),
(3, 50)
;
;WITH SummedVals AS (
SELECT ID,
SUM(value) / 30 Cnt
FROM Table1
GROUP BY ID
)
, Vals AS (
SELECT ID,
Cnt - 1 Cnt
FROM SummedVals
UNION ALL
SELECT ID,
Cnt - 1 Cnt
FROM Vals
WHERE Cnt > 0
)
SELECT ID,
NEWID()
FROM Vals
ORDER BY 1
SQL Fiddle DEMO

How to filter rows on a complex filter

I have these rows in a table
ID Name Price Delivery
== ==== ===== ========
1 apple 1 1
2 apple 3 2
3 apple 6 3
4 apple 9 4
5 orange 4 6
6 orange 5 7
I want to have the price at the third delivery (Delivery=3) or the last price if there's no third delivery.
It would give me this :
ID Name Price Delivery
== ==== ===== ========
3 apple 6 3
6 orange 5 7
I don't necessary want a full solution but an idea of what to look for would be greatly appreciated.
SQL> create table t (id,name,price,delivery)
2 as
3 select 1, 'apple', 1, 1 from dual union all
4 select 2, 'apple', 3, 2 from dual union all
5 select 3, 'apple', 6, 3 from dual union all
6 select 4, 'apple', 9, 4 from dual union all
7 select 5, 'orange', 4, 6 from dual union all
8 select 6, 'orange', 5, 7 from dual
9 /
Table created.
SQL> select max(id) keep (dense_rank last order by nullif(delivery,3) nulls last) id
2 , name
3 , max(price) keep (dense_rank last order by nullif(delivery,3) nulls last) price
4 , max(delivery) keep (dense_rank last order by nullif(delivery,3) nulls last) delivery
5 from t
6 group by name
7 /
ID NAME PRICE DELIVERY
---------- ------ ---------- ----------
3 apple 6 3
6 orange 5 7
2 rows selected.
EDIT: Since you want "an idea of what to look for", here is an description of why I think this solution is the best, besides being the query with the least amount of lines. Your expected result set indicates that you want to group your data per fruit name ("group by name"). And of each group you want to keep the values of the records with delivery = 3 or when that number doesn't exists, the last one ("keep (dense_rank last order by nullif(delivery,3) nulls last"). In my opinion, the query above just reads like that. And it uses only one table access to get the result, although my query is not unique in that.
Regards,
Rob.
Use ROW_NUMBER twice - once to filter the rows away that are after the third delivery, and the second time to find the last row remaining (i.e. a typical max per group query).
I've implemented this using CTEs. I tested it in SQL Server but I believe that Oracle supports the same syntax.
WITH T1 AS (
SELECT
ID, Name, Price, Delivery,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Delivery) AS rn
FROM Table1
), T2 AS (
SELECT
t1.*,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Delivery DESC) AS rn2
FROM T1
WHERE rn <= 3
)
SELECT ID, Name, Price, Delivery
FROM T2
WHERE rn2 = 1
Result:
ID Name Price Delivery
3 apple 6 3
6 orange 5 7
select t3.ID, t3.Name, t3.Price, t3.Delivery
from (
select Name, max(Delivery) as MaxDelivery
from MyTable
group by Name
) t1
left outer join MyTable t2 on t1.Name = t2.Name and Delivery = 3
inner join MyTable t3 on t1.Name = t3.name
and t3.Delivery = coalesce(t2.Delivery, t1.MaxDelivery)
Mark's and APC's answers work if you meant the third delivery, regardless of the Delivery number. Here's a solution using analytic functions that specifically searches for a record with Delivery = 3.
CREATE TABLE FRUITS (
ID NUMBER,
Name VARCHAR2(10),
Price INTEGER,
Delivery INTEGER);
INSERT INTO FRUITS VALUES (1, 'apple', 1, 1);
INSERT INTO FRUITS VALUES (2, 'apple', 3, 2);
INSERT INTO FRUITS VALUES (3, 'apple', 6, 3);
INSERT INTO FRUITS VALUES (4, 'apple', 9, 4);
INSERT INTO FRUITS VALUES (5, 'orange', 4, 6);
INSERT INTO FRUITS VALUES (6, 'orange', 5, 7);
INSERT INTO FRUITS VALUES (7, 'pear', 2, 5);
INSERT INTO FRUITS VALUES (8, 'pear', 4, 6);
INSERT INTO FRUITS VALUES (9, 'pear', 6, 7);
INSERT INTO FRUITS VALUES (10, 'pear', 8, 8);
SELECT ID,
Name,
Price,
Delivery
FROM (SELECT ID,
Name,
Price,
Delivery,
SUM(CASE WHEN Delivery = 3 THEN 1 ELSE 0 END)
OVER (PARTITION BY Name) AS ThreeCount,
ROW_NUMBER()
OVER (PARTITION BY Name ORDER BY Delivery DESC) AS rn
FROM FRUITS)
WHERE (ThreeCount <> 0 AND Delivery = 3) OR
(ThreeCount = 0 AND rn = 1)
ORDER BY ID;
DROP TABLE FRUITS;
And the results from Oracle XE 10g:
ID Name Price Delivery
---- ---------- ------- ----------
3 apple 6 3
6 orange 5 7
10 pear 8 8
I included a third fruit in the sample data to illustrate the effect of different interpretations of the question. The other solutions would pick ID=9 for the pear.