Counting columns if certain Id - sql

I have a table tblTitles that I am attempting to run a select query on. I would like to select a count based reports are there with IdState and do a count on how many of those titles belong to IsOnSaleCountId which would be if that column has an id of 1
Here is an example of the table:
+---------+----------+-------------------+-----------------+
| IdState | RegionId | Title | IsOnSaleId |
+---------+----------+-------------------+-----------------+
| 22 | 1 | Online Shopping | 0 |
| 22 | 1 | Retail Shopping | 1 |
| 22 | 1 | Pick Up | 0 |
| | | | |
+---------+----------+-------------------+-----------------+
My expected outcome should read that IdState of 22 has 3 reports and 1 report is onSale due to the 1 integer in the second row. Which would look similar to this:
+---------+-------------+---------------+
| IdState | ReportCount | IsOnSaleCount |
+---------+-------------+---------------+
| 22 | 3 | 1 |
+---------+-------------+---------------+
I am having issues when doing a select statement with this count. The IsOnSaleCount is identical to the ReportCount number which they should not be.
I believe this is the case due to my line of code of case when count(i.IsOnSaleId) > 0 THEN count(1) Else 0 End as IsOnSaleCount
Is this something that I can do in a SELECT query?
Here is an example of my query :
select
i.IdState,
count(i.RegionId) as ReportCount,
case when count(i.IsOnSaleId) > 0 THEN count(1) Else 0 End as IsOnSaleCount,
0 as EnterpriseReportCount,
i.IdReportCollection_PK_PrimaryCollection
from IBIS_Local.dbo.tblindustry i

If you want the count:
count(i.IsOnSaleId) as IsOnSaleCount,
If you just want a 0/1 flag, you could do:
sign(count(i.IsOnSaleId)) as IsOnSaleCount,

IF OBJECT_ID('stack.report') IS NOT NULL DROP TABLE stack.report
CREATE TABLE stack.report ( IdState TINYINT, RegionID TINYINT, Title VARCHAR(50), IsOnSaleId INT)
INSERT INTO stack.report
VALUES
(22,1,'Online Shopping', 0)
, (22,1,'Retail Shopping', 1)
, (22,1,'Pick Up', 0)
SELECT *, CONVERT(TINYINT, IsOnSaleId) isonsaleint FROM stack.report
SELECT IdState, COUNT(*) ReportCount, SUM(IsOnSaleId) OnSaleCount
FROM stack.report
GROUP BY IdState
ORDER BY IdState
Result
IdState | ReportCount | OnSaleCount
22 | 3 | 1
The SUM works if IsOnSaleId is an INT, SMALLINT or TINYINT. If IsOnSalesId datatype is BIT (commonly used for flags), then you will need to convert to one of the int types like this SUM(CONVERT(INT, IsOnSaleId))

Related

SQL Server: GROUP BY with multiple columns produces duplicate results

I'm trying to include a 3rd column into my existing SQL Server query but I am getting duplicate result values.
Here is an example of the data contained in tb_IssuedPermits:
| EmployeeName | Current |
|--------------|---------|
| Person A | 0 |
| Person A | 0 |
| Person B | 1 |
| Person C | 0 |
| Person B | 0 |
| Person A | 1 |
This is my current query which produces duplicate values based on 1 or 0 bit values.
SELECT EmployeeName, COUNT(*) AS Count, [Current]
FROM tb_IssuedPermits
GROUP BY EmployeeName, [Current]
| EmployeeName | Count | Current |
|--------------|-------|---------|
| Person A | 2 | 0 |
| Person B | 1 | 0 |
| Person C | 1 | 0 |
| Person A | 1 | 1 |
| Person B | 1 | 1 |
Any ideas on how I can amend my query to have the following expected result? I want one result row per EmployeeName. And Current shall be 1, if for the EmployeeName exists a row with Current = 1, else it shall be 0.
| EmployeeName | Count | Current |
|--------------|-------|---------|
| Person A | 3 | 1 |
| Person B | 2 | 1 |
| Person C | 1 | 0 |
The result does not need to be in any specific order.
TIA
If your Current column contains the string values 'FALSE' and 'TRUE' you can do this
SELECT EmployeeName, Count(*) AS Count,
MAX([Current]) AS Current
FROM tb_IssuedPermits
GROUP BY EmployeeName
It's a hack but it works: MAX will get the TRUE from each group if there is one.
If your Current column is a BIT, cast to INT and cast back, as #ThorstenKettner suggested.
SELECT EmployeeName,
Count(*) AS Count,
CAST(MAX(CAST([Current] AS INT)) AS BIT) AS Current
FROM tb_IssuedPermits
GROUP BY EmployeeName
Alternatively, you can use conditional aggregation:
SELECT EmployeeName,
Count(*) AS Count,
CAST(COUNT(NULLIF(Current, 0)) AS BIT) AS Current
FROM tb_IssuedPermits
GROUP BY EmployeeName
you can do like this
SELECT EmployeeName, Count(1) AS Count,SUM(CAST([Current]AS INT)) AS Current FROM tb_IssuedPermits GROUP BY EmployeeName

Count amount of same value

I have a simple task which I to be honest have no idea how to accomplish. I have these values from SQL query:
| DocumentNumber | CustomerID |
------------------------------
| AAA | 1 |
| BBB | 1 |
| CCC | 2 |
| DDD | 3 |
-------------------------------
I would like to display a bit modified table like this:
| DocumentNumber | CustomerID | Repeate |
-----------------------------------------
| AAA | 1 | Multiple |
| BBB | 1 | Multiple |
| CCC | 2 | Single |
| DDD | 3 | Single |
------------------------------------------
So, the idea is simple - I need to append a new column and set 'Multiple' and 'Single' value
depending on if customer Id exists multiple times
Use window functions:
select t.*,
(case when count(*) over (partition by CustomerId) = 1 then 'Single'
else 'Multiple'
end) as repeate
from t;
You also achieve the Same By using GROUP BY & SUB QUERY
DECLARE #T TABLE(
DocumentNumber VARCHAR(10),
CustomerID INT)
Insert Into #T VALUES('AAA', 1 ),('BBB', 1 ),('CCC', 2 ),('DDD', 3 )
select M.DocumentNumber,M.CustomerID,CASE WHEN Repeated_Row>1 THEN 'Multiple' ELSE 'Single' END As Repeate
from #T M
LEFT JOIN (SELECT CustomerID,COUNT(*) AS Repeated_Row FROM #T GROUP BY CustomerID) S ON S.CustomerID=M.CustomerID

Linking Related IDs together through two other ID columns

I have a table of about 100k rows with the following layout:
+----+-----------+------------+-------------------+
| ID | PIN | RAID | Desired Output ID |
+----+-----------+------------+-------------------+
| 1 | 80602627 | 1737852-1 | 1 |
| 2 | 80602627 | 34046655-1 | 1 |
| 3 | 351418172 | 33661 | 2 |
| 4 | 351418172 | 33661 | 2 |
| 5 | 351418172 | 33661 | 2 |
| 6 | 351418172 | 34443321-1 | 2 |
| 7 | 491863017 | 26136 | 3 |
| 8 | 491863017 | 34575 | 3 |
| 9 | 491863017 | 34575 | 3 |
| 10 | 661254727 | 26136 | 3 |
| 11 | 661254727 | 26136 | 3 |
| 12 | NULL | 7517 | 4 |
| 13 | NULL | 7517 | 4 |
| 14 | NULL | 7517 | 4 |
| 15 | NULL | 7517 | 4 |
| 16 | NULL | 7517 | 4 |
| 17 | 554843813 | 33661 | 2 |
| 18 | 554843813 | 33661 | 2 |
+----+-----------+------------+-------------------+
The ID column has unique values, with the PIN and RAID columns being two separate identifying numbers used to group linked IDs together. The Desired Output ID column is what I would like SQL to do, essentially looking at both the PIN and RAID columns to spot where there are any relationships between them.
So for example Where Desired Output ID = 2, IDs 3-6 match on PIN = 351418172, and then IDs 17-18 also match as the RAID of 33661 was in the rows for IDs 3-5.
To add as well, NULLs will be in the PIN Column but not in any others.
I did spot a similar question Text however as it is in BigQuery I wasnt sure it would help.
Have been trying to crack this one for a while with no luck, any help massively appreciated.
I suppose DENSE_RANK can solve your problem. Not sure what the combination of PIN and RAID should be, but I think you'll be able to figure it out how to do it like this:
SELECT *,DENSE_RANK( ) over (ORDER BY isnull(pin,id) ),DENSE_RANK( ) over (ORDER BY raid)
FROM accounts
I believe I have found a bit of a bodged solution to this. It runs very slowly as it goes row by row and will only go two links deep on PIN/RAID, but this should be sufficient for 99%+ cases.
Would appreciate any suggestions to speeding it up if anything is immediately obvious.
ID in post above is DebtorNo in Code:
DECLARE #Counter INT = 1
DECLARE #EndCounter INT = 0
IF OBJECT_ID('Tempdb..#OrigACs') IS NOT NULL
BEGIN
DROP TABLE #OrigACs
END
SELECT DebtorNo,
Name,
PostCode,
DOB,
RAJoin,
COALESCE(PIN,DebtorNo COLLATE DATABASE_DEFAULT) AS PIN,
RelatedAssets,
RAID,
PINRelatedAssets
INTO #OrigACs
FROM MIReporting..HC_RA_Test_Data RA
IF OBJECT_ID('Tempdb..#Accounts') IS NOT NULL
BEGIN
DROP TABLE #Accounts
END
SELECT *,
ROW_NUMBER() OVER (ORDER BY CAST(RA.DebtorNo AS INT)) AS Row
INTO #Accounts
FROM #OrigACs RA
ORDER BY CAST(RA.DebtorNo AS INT)
CREATE INDEX Temp_HC_Index ON #OrigACs (RAID,PIN)
SET #EndCounter = (SELECT MAX(Row) FROM #Accounts)
WHILE #Counter <= #EndCounter
BEGIN
IF OBJECT_ID('Tempdb..#RAID1') IS NOT NULL
BEGIN
DROP TABLE #RAID1
END
SELECT *
INTO #RAID1
FROM #OrigACs A
WHERE A.RAID IN (SELECT RAID FROM #Accounts WHERE [Row] = #Counter)
IF OBJECT_ID('Tempdb..#PIN1') IS NOT NULL
BEGIN
DROP TABLE #PIN1
END
SELECT *
INTO #PIN1
FROM #OrigACs A
WHERE A.PIN IN (SELECT PIN FROM #RAID1)
IF OBJECT_ID('Tempdb..#RAID2') IS NOT NULL
BEGIN
DROP TABLE #RAID2
END
SELECT *
INTO #RAID2
FROM #OrigACs A
WHERE A.RAID IN (SELECT RAID FROM #PIN1)
IF OBJECT_ID('Tempdb..#PIN2') IS NOT NULL
BEGIN
DROP TABLE #PIN2
END
SELECT *
INTO #PIN2
FROM #OrigACs A
WHERE A.PIN IN (SELECT PIN FROM #RAID2)
INSERT INTO MIReporting..HC_RA_Final_ACs
SELECT DebtorNo,
Name,
PostCode,
DOB,
RAJoin,
CASE
WHEN PIN = DebtorNo COLLATE DATABASE_DEFAULT THEN NULL
ELSE PIN
END AS PIN,
RelatedAssets,
RAID,
PINRelatedAssets,
COALESCE((SELECT MAX(FRAID) FROM MIReporting..HC_RA_Final_ACs),0) + 1 AS FRAID
FROM #PIN2
SET #Counter = (SELECT MIN([ROW]) FROM #Accounts O WHERE O.DebtorNo NOT IN (SELECT DebtorNo FROM MIReporting..HC_RA_Final_ACs));
END;
SELECT *
FROM MIReporting..HC_RA_Final_ACs
DROP TABLE #OrigACs
DROP TABLE #Accounts
DROP TABLE #RAID1
DROP TABLE #PIN1
DROP TABLE #RAID2
DROP TABLE #PIN2

Convert tuple value to column names

Got something like:
+-------+------+-------+
| count | id | grade |
+-------+------+-------+
| 1 | 0 | A |
| 2 | 0 | B |
| 1 | 1 | F |
| 3 | 1 | D |
| 5 | 2 | B |
| 1 | 2 | C |
I need:
+-----+---+----+---+---+---+
| id | A | B | C | D | F |
+-----+---+----+---+---+---+
| 0 | 1 | 2 | 0 | 0 | 0 |
| 1 | 0 | 0 | 0 | 1 | 1 |
| 2 | 0 | 5 | 1 | 0 | 0 |
I don't know if I can even do this. I can group by id but how would you read the count value for each grade column?
CREATE TABLE #MyTable(_count INT,id INT , grade VARCHAR(10))
INSERT INTO #MyTable( _count ,id , grade )
SELECT 1,0,'A' UNION ALL
SELECT 2,0,'B' UNION ALL
SELECT 1,1,'F' UNION ALL
SELECT 3,1,'D' UNION ALL
SELECT 5,2,'B' UNION ALL
SELECT 1,2,'C'
SELECT *
FROM
(
SELECT _count ,id ,grade
FROM #MyTable
)A
PIVOT
(
MAX(_count) FOR grade IN ([A],[B],[C],[D],[F])
)P
You need a "pivot" table or "cross-tabulation". You can use a combination of aggregation and CASE statements, or, more elegantly the crosstab() function provided by the additional module tablefunc. All basics here:
PostgreSQL Crosstab Query
Since not all keys in grade have values, you need the 2-parameter form. Like this:
SELECT * FROM crosstab(
'SELECT id, grade, count FROM table ORDER BY 1,2'
, $$SELECT unnest('{A,B,C,D,F}'::text[])$$
) ct(id text, "A" int, "B" int, "C" int, "D" int, "F" int);

PostgreSQL cross tab with three columns with values summed from one column

I am new to SQL and was trying to do a crosstab in Postgres. I would have done it in Excel, but I have a database of around 3.5 million rows, 20,000 different values for code, 7 categories in cat, and variable values from 1 to 100. A code may only have few of the 7 categories.
Excel can't handle the number of rows, so SQL it is.
My data is in the form
code | cat | value |
--------------------------------
abc123 | 1 | 4 |
abc234 | 2 | 6 |
abc345 | 1 | 1 |
abc123 | 3 | 2 |
abc123 | 6 | 12 |
with code and cat as text, value as integer stored in a Postgres table.
I would like to perform a crosstab on code and cat, with sum of value. I would like it to show zero instead of 'null' in the return, but if 'null' would be simpler query, then that would be fine.
So the output I would like is
code | 'cat=0' | 'cat=1' | 'cat=2' | 'cat=3' | 'cat=4' | 'cat=5' | 'cat=6'|
abc123 | 25 | 0 | 3 | 500 | 250 | 42 | 0 |
abc234 | 0 | 100 | 0 | 10 | 5 | 0 | 25 |
abc345 | 1000 | 0 | 0 | 0 | 0 | 0 | 0 |
I have searched on Postgres help files and other forums; the closest thing was the SO question PostgreSQL Crosstab Query but I couldn't figure out how to sum the values from third column.
Any assistance would be greatly appreciated.
I got this working by updating my code to the following:
select * from crosstab(
'select code, cat, sum(value) as value
from my_table
group by code, cat
order by 1,2'
) as ct(code varchar(255),
cat_0 bigint,
cat_1 bigint,
cat_2 bigint,
cat_3 bigint,
cat_4 bigint,
cat_5 bigint,
cat_6 bigint)
I was able to determine the right data type by running the select statement inside the crosstab and matching my as ct data types to those returned by the query inside the crosstab.
Try:
select * from crosstab(
'select code, cat, sum(value) as value
from my_table
group by code, cat
order by 1,2'
) as ct(code text,
cat_0 int,
cat_1 int,
cat_2 int,
cat_3 int,
cat_4 int,
cat_5 int,
cat_6 int)