SQL grouping by all the columns - sql

Is there any way to group by all the columns of a table without specifying the column names? Like:
select * from table group by *

The DISTINCT Keyword
I believe what you are trying to do is:
SELECT DISTINCT * FROM MyFooTable;
If you group by all columns, you are just requesting that duplicate data be removed.
For example a table with the following data:
id | value
----+----------------
1 | foo
2 | bar
1 | foo
3 | something else
If you perform the following query which is essentially the same as SELECT * FROM MyFooTable GROUP BY * if you are assuming * means all columns:
SELECT * FROM MyFooTable GROUP BY id, value;
id | value
----+----------------
1 | foo
3 | something else
2 | bar
It removes all duplicate values, which essentially makes it semantically identical to using the DISTINCT keyword with the exception of the ordering of results. For example:
SELECT DISTINCT * FROM MyFooTable;
id | value
----+----------------
1 | foo
2 | bar
3 | something else

If you are using SqlServer the distinct keyword should work for you. (Not sure about other databases)
declare #t table (a int , b int)
insert into #t (a,b) select 1, 1
insert into #t (a,b) select 1, 2
insert into #t (a,b) select 1, 1
select distinct * from #t
results in
a b
1 1
1 2

I wanted to do counts and sums over full resultset. I achieved grouping by all with GROUP BY 1=1.

Short answer: no. GROUP BY clauses intrinsically require order to the way they arrange your results. A different order of field groupings would lead to different results.
Specifying a wildcard would leave the statement open to interpretation and unpredictable behaviour.

nope. are you trying to do some aggregation? if so, you could do something like this to get what you need
;with a as
(
select sum(IntField) as Total
from Table
group by CharField
)
select *, a.Total
from Table t
inner join a
on t.Field=a.Field

No because this fundamentally means that you will not be grouping anything. If you group by all columns (and have a properly defined table w/ a unique index) then SELECT * FROM table is essentially the same thing as SELECT * FROM table GROUP BY *.

Here is my suggestion:
DECLARE #FIELDS VARCHAR(MAX), #NUM INT
--DROP TABLE #FIELD_LIST
SET #NUM = 1
SET #FIELDS = ''
SELECT
'SEQ' = IDENTITY(int,1,1) ,
COLUMN_NAME
INTO #FIELD_LIST
FROM Req.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = N'new340B'
WHILE #NUM <= (SELECT COUNT(*) FROM #FIELD_LIST)
BEGIN
SET #FIELDS = #FIELDS + ',' + (SELECT COLUMN_NAME FROM #FIELD_LIST WHERE SEQ = #NUM)
SET #NUM = #NUM + 1
END
SET #FIELDS = RIGHT(#FIELDS,LEN(#FIELDS)-1)
EXEC('SELECT ' + #FIELDS + ', COUNT(*) AS QTY FROM [Req].[dbo].[new340B] GROUP BY ' + #FIELDS + ' HAVING COUNT(*) > 1 ')

You can use Group by All but be careful as Group by All will be removed from future versions of SQL server.

Related

How to get Output parameter from a column in table for a stored procedure

I've stored procedure like this:
create procedure sp_testsp
(
#vc_order_by varchar(100),
#int_start_index INT,
#int_grid_size INT,
#count bigint output
)
as
begin
select * from
(select ROW_NUMBER() over
(order by
case #vc_order_by = '' then tab1.int_id end desc) AS row,
*,
COUNT(tab1.int_id) OVER() as totalRowCount
from
(select * from tbl_test) tab1) tab2
where row BETWEEN CONVERT(VARCHAR, #int_start_index) and CONVERT(VARCHAR,(#int_start_index-1) + #int_grid_size);
set #count = 0;
end
We can execute the above stored procedure by:
DECLARE #size bigint;
EXEC sp_testsp '', 1,5, #size output;
SELECT #size;
The written sp provides data based on pagination and we can retrieve 100 or any number of records by passing a number in #int_grid_size .
The table output looks like following:
row int_id vc_name totalRowCount
1 5 a 107
2 6 ab 107
3 7 abc 107
4 8 abcd 107
5 10 abcc 107
The last column gives the total records count of the table or total record if we use where condition.
I want to OUTPUT any one column value of the totalRowCount in '#count' in the stored procedure.
I cannot use ##ROWCOUNT as it only sends the count of records the sp is outputting i.e in this case 5 but actual records are 107.
Just wondering if there is any way. Any help is apperciated. Thanks.
Edit:
I tried something like this, and it works:
create procedure sp_testsp
#param1 nvarchar(800),
#count bigint output
as
begin
select * from tbl_test tt where tt.col1 = #param1;
set #count = select Count(*) from tbl_test tt where tt.col1 = #param1;
end
The issue with this is I've to call the query once and then call the query again for #count. This is working but taking lot of time for big queries.
You can do that by temp table
select * into #temp from
(select ROW_NUMBER() over
(order by
case #vc_order_by = '' then tab1.int_id end desc) AS row,
*,
COUNT(tab1.int_id) OVER() as totalRowCount
from
(select * from tbl_test) tab1) tab2
where row BETWEEN CONVERT(VARCHAR, #int_start_index) and CONVERT(VARCHAR,(#int_start_index-1) + #int_grid_size);
select top 1 #count=totalRowCount from #temp
select * from #temp --you can exclude totalRowCount

Resultset with pivot table as sub query - MSSQL

I have two tables with following structure.
and
and I want results like this from one query.
CUSTOMER_CODE | CUSTOMER_NAME | LINE 1 |LINE 2 | LINE 3
we have to make first table as pivot, but how not sure.
Please advise.
Thanks
Here is a dynamic conditional aggregation There were no table names, so TABLE1 relates to Image1
Declare #SQL varchar(max)=''
Select #SQL = #SQL+',[Line '+cast([Line#] as varchar(25))+']=max(case when [Line#]='+cast([Line#] as varchar(25))+' then EMail else '''' end)'
From (Select Distinct [Line#] from Table1) A
Order By [Line#]
Select #SQL='
Select A.Customer_Code
,B.Customer_Branch_Name'+#SQL+'
From Table1 A
Join Table2 B
on A.Customer_Code=B.Customer_Branch
Group By A.Customer_Code,B.Customer_Branch_Name'
Exec(#SQL)
Returns

Multiple Filter in the same column

I have a sql table with some values and a lot of filters
ID | Name | Filter1 | Filter2 | Filter3 | Filter4 ... and so on...
As now the filters have been set as int and I am running a query as follows to get the data required
select Name
from tblABC
where Filter1=1 and Filter2 = 7 and Filter3 = 33 ... and so on...'
My issue is that I want a filter column to hold multiple numbers. eg:- row no 3 will have numbers 8 and 13 in Filter1 cell, so that when I run a query for 8 or 13 I get the same result.
ie I want both the below queries to return the same result.
select... where Filter1=8
select... where Filter1=13
How can this be done? I tried converting the Filter columns to nvarchar and entering data as .8.13. where '.' where was used as separators. After this, running a query 'select... where Filter1 LIKE '%.8.%' is working for me.. But there are like 12 Filter columns and when such a string search is run in large volumes, wouldn't it make the query slow. What would be a more efficient way of doing this?
I am using Microsoft SQL 2014
A more efficient way, hmm. Separating tblABC from the filters would be my suggested way to go, even if it's not the most efficient way it will make up for it in maintenance (and it sure is more efficient than using like with wildcards for it).
tblABC ID Name
1 Somename
2 Othername
tblABCFilter ID AbcID Filter
1 1 8
2 1 13
3 1 33
4 2 5
How you query this data depends on your required output of course. One way is to just use the following:
SELECT tblABC.Name FROM tblABC
INNER JOIN tblABCFilter ON tblABC.ID = tblABCFilter.AbcID
WHERE tblABCFilter.Filter = 33
This will return all Name with a Filter of 33.
If you want to query for several Filters:
SELECT tblABC.Name FROM tblABC
INNER JOIN tblABCFilter ON tblABC.ID = tblABCFilter.AbcID
WHERE tblABCFilter.Filter IN (33,7)
This will return all Name with Filter in either 33 or 7.
I have created a small example fiddle.
I'm going to post a solution I use. I use a split function ( there are a lot of SQL Server split functions all over the internet)
You can take as example
CREATE FUNCTION [dbo].[SplitString]
(
#List NVARCHAR(MAX),
#Delim VARCHAR(255)
)
RETURNS TABLE
AS
RETURN ( SELECT [Value] FROM
(
SELECT
[Value] = LTRIM(RTRIM(SUBSTRING(#List, [Number],
CHARINDEX(#Delim, #List + #Delim, [Number]) - [Number])))
FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_objects) AS x
WHERE Number <= LEN(#List)
AND SUBSTRING(#Delim + #List, [Number], LEN(#Delim)) = #Delim
) AS y
);
and run your query like this
select Name
from tblABC
where Filter1 IN (
SELECT * FROM SplitString(#DatatoFilter,',') and
Filter2 (IN (
SELECT * FROM SplitString(#DatatoFilter,',') and
..so on.
If you have hunderds of thousands of records it may not perform very well. But it should work.
My personal aproch would be a stored procedure and temp tables. Create a temp table with all the values you want to use as filter
SELECT *
INTO #Filter1
FROM SplitString(#DatatoFilter,',')
SELECT *
INTO #Filter2
FROM SplitString(#DatatoFilter,',')
then the final select
SELECT * FROM yourtable
WHERE Filter1 IN (SELECT DISTINCT Part FROM #Filter1) and
Filter2 IN (SELECT DISTINCT Part FROM #Filter2)
I don't think it makes any big difference from the first query, but it is easier to read.
Another solution which you can try is to convert the columns to XML. Its better than converting the columns to VARCHAR. You can use .exist to get only the records matching your criteria. Something like this.
DECLARE #table1 TABLE
(
[ID] int, [Name] varchar(9),Filter1 XML
)
INSERT INTO #table1
([ID], [Name],Filter1)
VALUES
(1, 'Somename','<Filter>8</Filter>'),
(2, 'Othername','<Filter>8</Filter><Filter>13</Filter>'),
(3, 'Thirdname','<Filter>25</Filter>')
DECLARE #FilterValue INT = 8
SELECT Filter1.query('/Filter'),*
FROM #table1
WHERE Filter1.exist('/Filter[. = sql:variable("#FilterValue")]') = 1
EDIT
You can even use the XML column to store all 12 of your filters. So this filter xml column which store all your filters and their multiple values.
DECLARE #table1 TABLE
(
[ID] int, [Name] varchar(9),Filter XML
)
INSERT INTO #table1
([ID], [Name],Filter)
VALUES
(1, 'Somename','<Filter ID = "1"><FilterVal>8</FilterVal></Filter><Filter ID = "2"><FilterVal>3</FilterVal><FilterVal>12</FilterVal></Filter>'),
(2, 'Othername','<Filter ID = "1"><FilterVal>8</FilterVal><FilterVal>13</FilterVal></Filter><Filter ID = "2"><FilterVal>8</FilterVal><FilterVal>13</FilterVal></Filter>'),
(3, 'Thirdname','<Filter ID = "2"><FilterVal>12</FilterVal><FilterVal>25</FilterVal></Filter><Filter ID = "3"><FilterVal>33</FilterVal></Filter>')
DECLARE #Filter1Value INT = 8
DECLARE #Filter2Value INT = 12
SELECT *
FROM #table1
WHERE Filter.exist('/Filter[#ID = 1]/FilterVal[. = sql:variable("#Filter1Value")]') = 1
AND Filter.exist('/Filter[#ID = 2]/FilterVal[. = sql:variable("#Filter2Value")]') = 1

How to compare two sub queries in one sql statement

I have a table tbl_Country, which contains columns called ID and Name. The Name column has multiple country names separated by comma, I want the id when I pass multiple country names to compare with Name column values. I am splitting the country names using a function - the sample query looks like this:
#country varchar(50)
SELECT *
FROM tbl_Country
WHERE (SELECT *
FROM Function(#Country)) IN (SELECT *
FROM Function(Name))
tbl_country
ID Name
1 'IN,US,UK,SL,NZ'
2 'IN,PK,SA'
3 'CH,JP'
parameter #country ='IN,SA'
i have to get
ID
1
2
NOTE: The Function will split the string into a datatable
Try this
SELECT * FROM tbl_Country C
LEFT JOIN tbl_Country C1 ON C1.Name=C.Country
Try this:
SELECT *
FROM tbl_Country C
WHERE ',' + #country + ',' LIKE '%,' + C.Name + ',%';
Basically, by specifying multiple values in a single column, you are violating the 1st NF. Therefore, the following might not be a good approach but provides the solution that you are looking for:
declare #country varchar(50)= 'IN,SA'
declare #counterend int
declare #counterstart int =1
declare #singleCountry varchar(10)
set #counterend = (select COUNT(*) from fnSplitStringList(#country))
create table #temp10(
id int
,name varchar(50))
while #counterstart<= #counterend
begin
;with cte as (
select stringliteral country
, ROW_NUMBER() over (order by stringliteral) countryseq
from fnSplitStringList(#country))
select #singleCountry = (select country FROM cte where countryseq=#counterstart)
insert into #temp10(id, name)
select * from tbl_country t1
where not exists (select id from #temp10 t2 where t1.id=t2.id)
and name like '%' + #singleCountry +'%'
set #counterstart= #counterstart+1
end
select * from #temp10
begin drop table #temp10 end
How it works: It splits the passed string and ranks it. Afterwards, it loops through all the records for every single Value(country) produced and inserts them into temptable.
try this,
select a.id FROM tbl_Country a inner join
(SELECT country FROM dbo.Function(#Country)) b on a.name=b.country

SQL Server 2008 + Creating a cross-tab stored procedure

I have two tables that I want to join and create a crosstab table in SQL 2008:
TableA:
Auto_ID | Fiscal_Period | Amount
1 | 01012012 | NULL
1 | 01022012 | 80
1 | 01032012 | NULL
2 | 01012012 | NULL
2 | 01022012 | 10
TABLEB:
Auto_ID | Row_ID | StaticData
1 | 1 | sampledata
2 | 2 | data1
I would like to use cross table to dynamic create the following table structure:
Row_ID | StaticData | FiscalPeriod(01012012) | FiscalPeriod(01022012) | FiscalPeriod(01032012)
1 | sampledata | NULL | 80 | NULL
2 | data1 | NULL | 10 | NULL
My current query joins the tables correctly; however, I am having difficulty transposing the fiscal periods into my header row.
SELECT *
FROM (SELECT
B.Row_Id as RowID, B.StaticData as StaticData, A.Fiscal_Period AS FPPD
FROM TableA A
LEFT JOIN TableB B ON A.Auto_ID = B.Auto_ID)
This is what I would do:
First create some test data:
CREATE TABLE tblA (Auto_ID INT,Fiscal_Period VARCHAR(100),Amount FLOAT)
CREATE TABLE tblB (Auto_ID INT,Row_ID INT,StaticData VARCHAR(100))
INSERT INTO tblA
SELECT 1,'01012012',NULL UNION ALL
SELECT 1,'01022012',80 UNION ALL
SELECT 1,'01032012',NULL UNION ALL
SELECT 2,'01012012',NULL UNION ALL
SELECT 2,'01022012',10
INSERT INTO tblB
SELECT 1,1,'sampledata' UNION ALL
SELECT 2,2,'data1'
Then find the unique columns :
DECLARE #cols VARCHAR(MAX)
;WITH CTE
AS
(
SELECT
ROW_Number() OVER(PARTITION BY tblA.Fiscal_Period ORDER BY tblA.Fiscal_Period) AS RowNbr,
tblA.Fiscal_Period
FROM
tblA AS tblA
)
SELECT
#cols = COALESCE(#cols + ','+QUOTENAME('FiscalPeriod('+Fiscal_Period+')'),
QUOTENAME('FiscalPeriod('+Fiscal_Period+')'))
FROM
CTE
WHERE
CTE.RowNbr=1
Then execute a pivot with dynamic sql:
DECLARE #query NVARCHAR(4000)=
N'SELECT
*
FROM
(
SELECT
tblB.Row_ID,
tblb.StaticData,
''FiscalPeriod(''+tblA.Fiscal_Period+'')'' AS Name,
tblA.Amount
FROM
tblA AS tblA
JOIN tblB AS tblB
ON tblA.Auto_ID=tblB.Auto_ID
) AS p
PIVOT
(
SUM(Amount)
FOR Name IN ('+#cols+')
) AS Pvt'
EXECUTE(#query)
Then in my case I will drop the temp tables:
DROP TABLE tblA
DROP TABLE tblB
I hope this will help you
Since you didn't specify a flavour of database, please mind, that the following is valid only for MySQL!
A cross-tab query is possible only with a very dirty trick, it is only practically feasable in a stored proc.
You start by thinking out some way, to transform a list of fiscal periods into SQL, something like
SELECT
TABLEB.Row_ID,
TABLEB.staticdata
,fp01012012.Amount as fp01012012amount
,fp01022012.Amount as fp01022012amount
,fp01032012.Amount as fp01032012amount
FROM
TABLEB
LEFT JOIN TableA AS fp01012012 ON fp01012012.Auto_ID=TABLEB.Auto_ID AND fp01012012.Fiscal_Period='01012012'
LEFT JOIN TableA AS fp01022012 ON fp01022012.Auto_ID=TABLEB.Auto_ID AND fp01022012.Fiscal_Period='01022012'
LEFT JOIN TableA AS fp01032012 ON fp01032012.Auto_ID=TABLEB.Auto_ID AND fp01032012.Fiscal_Period='01032012'
Which you now have to build as dynamic SQL - this is feasable only in a stored proc.
DELIMITER $$
DROP PROCEDURE IF EXISTS `create_fiscal_data`$$
CREATE PROCEDURE `create_fiscal_data` ()
BEGIN
DECLARE dynfields VARCHAR(10000) DEFAULT 'SELECT TABLEB.Row_ID, TABLEB.staticdata';
DECLARE dynfrom VARCHAR(10000) DEFAULT ' FROM TABLEB';
DECLARE period VARCHAR(10) DEFAULT '';
DECLARE done INT DEFAULT 0;
DECLARE id INT DEFAULT 7;
DECLARE periods CURSOR FOR SELECT DISTINCT Fiscal_Period FROM TableA;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
OPEN periods;
cycleperiods: LOOP
FETCH periods INTO period;
IF done=1 THEN LEAVE cycleperiods; END IF;
SET dynfields=CONCAT(dynfields,',`fp',period,'`.Amount AS `fp',period,'amount`');
SET dynfrom=CONCAT(dynfrom,' LEFT JOIN TableA AS `fp',period,'` ON `fp',period,'`.Auto_ID=TABLEB.Auto_ID AND `fp',period,'`.Fiscal_Period="',period,'"');
END LOOP;
CLOSE periods;
SELECT #dynsql:=CONCAT(dynfields,dynfrom) INTO dynfields;
-- Here comes the trick!
PREPARE dynqry FROM #dynsql;
EXECUTE dynqry;
END$$
DELIMITER ;
The trick is, to build the SQL into the variable #dynsql (DECLAREd variables won't work), then prepare and execute it.
Now the query
CALL `create_fiscal_data;`
Will create the output you need.