SQL count one field two times in select with different parameters - sql

I like to have my query count one column two times in my select based on the value. So for example.
input: table
id | type
-------------|-------------
1 | 1
2 | 1
3 | 2
4 | 2
5 | 2
output: query (in 1 row, not two):
countfirst = 2 (two times 1)
countsecond = 3 (three times 2)
An default count in an select counts all rows in the query. But i like to count rows based
on an number without limiting the query. When using for example WHERE type = '1', type 2
gets filtered and cannot be counted anymore.
Is there an solution for this case in SQL?
--- EXAMPLE USE (situation above is simplefied but case is the same) ---
With one query i get all cars grouped by type from an table. There are two type signs: yellow (in db 1) and grey (in db 2). So in that query i have the folowing output:
Renault - ten times found - two yellow signs - eight grey signs

Create a table, script is given below.
CREATE TABLE [dbo].[temptbl](
[id] [int] NULL,
[type] [int] NULL
) ON [PRIMARY]
Execute the insert script as
insert into [temptbl] values(1,1)
insert into [temptbl] values(2,1)
insert into [temptbl] values(3,2)
insert into [temptbl] values(4,2)
insert into [temptbl] values(5,2)
Then execute the query.
;WITH cte as(
SELECT [type], Count([type]) cnt
FROM temptbl
GROUP BY [type]
)
SELECT * FROM cte
pivot (Sum([cnt]) for [type] in ([1],[2])) as AvgIncomePerDay

You can use the GROUP BY clause as Mureinik suggested, but with the addition of a WHERE clause to filter the results.
Below shows the results for type = 1 (assuming type is an INT:
SELECT type, COUNT(*) AS NoOfRecords
FROM table
WHERE type IN (1)
GROUP BY type
So if we wanted 1 and 2 we can use:
SELECT type, COUNT(*) AS NoOfRecords
FROM table
WHERE type IN (1, 2)
GROUP BY type
Lastly, that IN statement can pull type from another query:
SELECT type, COUNT(*) AS NoOfRecords
FROM table
WHERE type IN (SELECT type FROM someOtherTable)
GROUP BY type

Related

SQL - List all pages in between record while maintaining ID key

I'm trying to come up with a useful way to list all pages in between the first of last page of a document into new rows while maintaining the ID number as a key, or cross reference. I have a few ways of getting pages in between, but I'm not exactly sure how to maintain the key in a programmatic way.
Example Input:
First Page Last Page ID
ABC_001 ABC_004 1
ABC_005 ABC_005 2
ABC_006 ABC_010 3
End Result:
All Pages ID
ABC_001 1
ABC_002 1
ABC_003 1
ABC_004 1
ABC_005 2
ABC_006 3
ABC_007 3
ABC_008 3
ABC_009 3
ABC_010 3
Any help is much appreciated. I'm using SQL mgmt studio.
One approach would be to set up a numbers table, that contains a list of numbers that you may possibly find in the column content:
CREATE TABLE numbers( idx INTEGER);
INSERT INTO numbers VALUES(1);
INSERT INTO numbers VALUES(2);
...
INSERT INTO numbers VALUES(10);
Now, assuming that all page values have 7 characters, with the last 3 being digits, we can JOIN the original table with the numbers table to generate the missing records:
SELECT
CONCAT(
SUBSTRING(t.First_Page, 1, 4),
REPLICATE('0', 3 - LEN(n.idx)),
n.idx
) AS [ALl Pages],
t.id
FROM
mytable t
INNER JOIN numbers n
ON n.idx >= CAST(SUBSTRING(t.First_Page, 5, 3) AS int)
AND n.idx <= CAST(SUBSTRING(t.Last_Page, 5, 3) AS int)
This demo on DB Fiddle with your sample data returns:
ALl Pages | id
:-------- | -:
ABC_001 | 1
ABC_002 | 1
ABC_003 | 1
ABC_004 | 1
ABC_005 | 2
ABC_006 | 3
ABC_007 | 3
ABC_008 | 3
ABC_009 | 3
ABC_010 | 3
To find all pages from First Page to Last Page per Book ID, CAST your page numbers from STRING to INTEGER, then add +1 to each page number until you reach the Last Page.
First, turn your original table into a table variable with the Integer data types using a TRY_CAST.
DECLARE #Book TABLE (
[ID] INT
,[FirstPage] INT
,[LastPage] INT
)
INSERT INTO #Book
SELECT [ID]
,TRY_CAST(RIGHT([FirstPage], 3) AS int) AS [FirstPage]
,TRY_CAST(RIGHT([LastPage], 3) AS int) AS [LastPage]
FROM [YourOriginalTable]
Set the maximum page that your pages will increment to using a variable. This will cap out your results to the correct number of pages. Otherwise your table would have many more rows than you need.
DECLARE #LastPage INT
SELECT #LastPage = MAX([LastPage]) FROM #Book
Turning a three-column table (ID, First Page, Last Page) into a two-column table (ID, Page) will require an UNPIVOT.
We're tucking that UNPIVOT into a CTE (Common Table Expression: basically a smart version of a temporary table (like a #TempTable or #TableVariable, but which you can only use once, and is a little more efficient in certain circumstances).
In addition to the UNPIVOT of your [First Name] and [Last Name] columns into a tall table, we're going to append every other combination of page number per ID using a UNION ALL.
;WITH BookCTE AS (
SELECT [ID]
,[Page]
FROM (SELECT [ID]
,[FirstPage]
,[LastPage]
FROM #Book) AS bp
UNPIVOT
(
[Page] FOR [Pages] IN ([FirstPage], [LastPage])
) AS up
UNION ALL
SELECT [ID], [Page] + 1 FROM BookCTE WHERE [Page] + 1 < #LastPage
)
Now that your data is held in a table format using a CTE with all combinations of [ID] and [Page] up to the maximum page in your #Book table, it's time to join your CTE with the #Book table.
SELECT DISTINCT
cte.ID
,cte.Page
FROM BookCTE AS cte
INNER JOIN #Book AS bk
ON bk.ID = cte.ID
WHERE cte.Page <= bk.[LastPage]
ORDER BY
cte.ID
,cte.Page
OPTION (MAXRECURSION 10000)
See also:
How to generate a range of numbers between two numbers (I based my code off of #Jayvee's answer)
Assigning variables using SET vs SELECT
SQL Server UNPIVOT
SQL Server CTE Basics
Recursive CTEs Explained
Note: will update with re-integrating string portion of FirstPage and LastPage (which I assume is based on book title). Stand by.

Rotate rows into columns with column names not coming from the row

I've looked at some answers but none of them seem to be applicable to me.
Basically I have this result set:
RowNo | Id | OrderNo |
1 101 1
2 101 10
I just want to convert this to
| Id | OrderNo_0 | OrderNo_1 |
101 1 10
I know I should probably use PIVOT. But the syntax is just not clear to me.
The order numbers are always two. To make things clearer
And if you want to use PIVOT then the following works with the data provided:
declare #Orders table (RowNo int, Id int, OrderNo int)
insert into #Orders (RowNo, Id, OrderNo)
select 1, 101, 1 union all select 2, 101, 10
select Id, [1] OrderNo_0, [2] OrderNo_1
from (
select RowNo, Id, OrderNo
from #Orders
) SourceTable
pivot (
sum(OrderNo)
for RowNo in ([1],[2])
) as PivotTable
Reference: https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-2017
Note: To build each row in the result set the pivot function is grouping by the columns not begin pivoted. Therefore you need an aggregate function on the column that is being pivoted. You won't notice it in this instance because you have unique rows to start with - but if you had multiple rows with the RowNo and Id you would then find the aggregation comes into play.
As you say there are only ever two order numbers per ID, you could join the results set to itself on the ID column. For the purposes of the example below, I'm assuming your results set is merely selecting from a single Orders table, but it should be easy enough to replace this with your existing query.
SELECT o1.ID, o1.OrderNo AS [OrderNo_0], o2.OrderNo AS [OrderNo_1]
FROM Orders AS o1
INNER JOIN Orders AS o2
ON (o1.ID = o2.ID AND o1.OrderNo <> o2.OrderNo)
From your sample data, simplest you can try to use min and MAX function.
SELECT Id,min(OrderNo) OrderNo_0,MAX(OrderNo) OrderNo_1
FROM T
GROUP BY Id

A thought experiment in SQL

I want to show the number of times each distinct element in a column in a table in a SQL database appears, alongside the particular distinct element in a new output table. Is it possible in a single statement over ramming my head over it manually?
Without having actually tried, how about this:
SELECT tmp.Field, (SELECT COUNT(*) FROM [Table] t WHERE t.DesiredField = tmp.Field) AS Count
FROM
(
SELECT DISTINCT DesiredField FROM [Table]
) tmp
This would first select all distinct values from [Table] and in the outer select, take the values and the number of times they appear in the column.
You could also try
SELECT Field, SUM(1) AS Count FROM Table
GROUP BY Field
This should "flatten" the table so that it only contains distinct values in Field and the number of rows where Field has the same value.
I just tried the second - it seems to work nicely.
Turns out I was wrong all the time. The second example and the following actually return the same results:
SELECT Field, COUNT(*) AS Count FROM Table
GROUP BY Field
Simplest just to use COUNT(). You'll see varieties on what your count parameter, so here are the options.
DECLARE #tbl TABLE(id INT, data INT)
INSERT INTO #tbl VALUES (1,1),(2,1),(3,2),(4,NULL)
SELECT data
,COUNT(*) Count_star
,COUNT(id) Count_id
,COUNT(data) Count_data
,COUNT(1) Count_literal
FROM #tbl
GROUP BY data
data Count_star Count_id Count_data Count_literal
----------- ----------- ----------- ----------- -------------
NULL 1 1 0 1
1 2 2 2 2
2 1 1 1 1
Warning: Null value is eliminated by an aggregate or other SET operation.
You'll see the difference coming with the treatment of NULL if you COUNT a field that contains NULLs.

How to add sequence number for groups in a SQL query without temp tables

I've created a complex search query in SQL 2008 that returns data sorted by groups, and the query itself has paging and sorting functions in it, but rather than returning a set number of records based on the paging options, it needs to return a set number of groups (so the number of records will vary).
I'm currently doing this through the use of Temp Tables (the first temp table creates a list of the Groups that will be selected as part of the search, and then numbers them... and the second query joins this table to the actual search... so, it ends up running the search query twice).
What I'm looking for is a more efficient way to do this using some of the new functions in SQL 2008 (which wouldn't require the use of temp tables).
If I can get the data in a format like this, I'd be set...
Record Group GroupSequence
-------|---------|--------------
1 Chickens 1
2 Chickens 1
3 Cows 2
4 Horses 3
5 Horses 3
6 Horses 3
Any ideas on how to accomplish this with a single query in SQL 2008, without using temp tables?
Sample data
create table sometable([group] varchar(10), id int, somedata int)
insert sometable select 'Horses', 9, 11
insert sometable select 'chickens', 19, 121
insert sometable select 'Horses', 29, 123
insert sometable select 'chickens', 49, 124
insert sometable select 'Cows', 98, 1
insert sometable select 'Horses', 99, 2
Query
select
Record = ROW_NUMBER() over (order by [Group], id),
[Group],
GroupSequence = DENSE_RANK() over (order by [Group])
from sometable
Output
Record Group GroupSequence
-------------------- ---------- --------------------
1 chickens 1
2 chickens 1
3 Cows 2
4 Horses 3
5 Horses 3
6 Horses 3
Without more details about the tables you have, I'd say look into CTE queries and the row_number function... something along the lines of:
;with groups as (
select top 10 name, row_number() over(order by name) 'sequence'
from table1
group by name
order by name
)
select row_number() over(order by g.name) 'Record',
g.name 'GroupName',
g.sequence 'GroupSequence'
from groups

How do I select rows in table (A) sharing the same foreign key (itemId) where multiple rows in table have the values in table B

Sorry about the title, not sure how to describe without example. I trying to implement faceting of attributes in SQL Server 2008.
I have 2 tables. itemAttributes and facetParameters
Assume the following values in itemAttributes
id, itemId, name, value
---------------------------------------
1 1 keywords example1
2 1 keywords example2
3 2 color red
4 2 keywords example1
5 2 keywords example2
6 3 keywords example2
7 3 color red
8 3 color blue
Assume the following values in facetParameters
name value
----------------------
keywords example1
color red
I need to retrieve the (optional: distinct) itemIds where a given itemId has rows that contain all the values in facetParameters.
e.g. given the rows in facetParameters the query should return itemId 2. At the moment I would be using this in a CTE however given that they do not support a number of features I can work around this if there is no solution that works inside a CTE.
I have done a fair bit of sql over the years but this one has really stumped me and the shame is I keep thinking the answer must be simple.
You could join both tables, and use a having clause to ensure that all items match:
select ia.itemid
from #itemAttributes ia
inner join #facetParameters fp
on ia.name = fp.name
and ia.value = fp.value
group by ia.itemid
having count(distinct fp.name) =
(
select count(*) from #facetParameters
)
The count in the having clause assumes that the name uniquely identifies a row in the facetParameters table. If it doesn't, add an identity column to facetParameters, and use count(distinct id_column) instead of count(distinct fp.name).
Here's code to create the data set in the question:
declare #itemAttributes table (id int, itemId int,
name varchar(max), value varchar(max))
insert into #itemAttributes
select 1,1,'keywords','example1'
union all select 2,1,'keywords','example2'
union all select 3,2,'color','red'
union all select 4,2,'keywords','example1'
union all select 5,2,'keywords','example2'
union all select 6,3,'keywords','example2'
union all select 7,3,'color','red'
union all select 8,3,'color','blue'
declare #facetParameters table (name varchar(max), value varchar(max))
insert into #facetParameters
select 'keywords','example1'
union all select 'color','red'