Pivoting rows to columns depending on data from another table - sql-server

I have two tables VisitorsPerDay and Languages as follows:
Languages table
| Code | Alias |
|---------------------|------------------|
| EN | English |
| AR | Arabic |
| FR | French |
| JP | Japanese |
VisitorsPerDay table
| Date | VisitorLanguage | Count |
|---------------------|------------------|-------------|
| 10/1/2019 | EN | 20 |
| 10/1/2019 | EN | 10 |
| 10/1/2019 | AR | 5 |
| 15/1/2019 | FR | 1 |
What the result should be is aggregated data for each day and two columns for each language in the languages table dynamically in which if a new language has been added there will be no need to edit the stored procedure
| Date | TotalVisits | En Visits | En AVG Visit % |
|---------------------|------------------|-------------|------------------|
| 10/1/2019 | 35 | 30 | 85% |
| 15/1/2019 | 1 | 0 | 0% |
What I have done is created a dynamic query and a cursor that loop over the languages and generate the require SQL statements for each language and append it to the dynamic query
What I want to know is there a better way to get the result set or is a dynamic query OK?

You have to use Dynamic SQL, the query will be ugly and not easy to maintain
declare #sql nvarchar(max)
select #sql = isnull(#sql + ',', 'SELECT [Date], TotalVisits = sum([Count]),' + char(13))
+ 'SUM(CASE WHEN VisitorLanguage = ''' + Code + ''' THEN [Count] END) AS [' + Code + ' Visits],'+ char(13)
+ 'SUM(CASE WHEN VisitorLanguage = ''' + Code + ''' THEN [Count] END) * 100 / SUM([Count]) AS [' + Code + ' AVG Visits %]'+ char(13)
from Languages
select #sql = #sql + 'FROM VisitorsPerDay GROUP BY [Date]'
-- print out the dynmamic query
print #sql
exec sp_executesql #sql

Related

How can to get distinct data of all the columns in a table in spearte result set in SQL

I have created a Stored Procedure to get distinct data of all the columns. But I have to Specify each column name of the respective table.
Bur I don't want to specify each column name of the table and get the distinct data of all the columnn in separte result set.
+----+------+---------+-----------+
| Id | name | Address | City |
+----+------+---------+-----------+
| 1 | A | Max | Rajasthan |
| 2 | A | Min | Delhi |
| 1 | A | Max | Rajathan |
| 1 | A | Min | UP |
+----+------+---------+-----------+
This is the code of my Stored Procedure for getting different result set of each column
create proc sp_task1 #table varchar(20)
as
begin
exec('
select distinct id FROM ' +#table+'
')
exec('
select distinct name FROM ' +#table+'
')
exec('
select distinct address FROM ' +#table+'
')
exec('
select distinct city FROM ' +#table+'
')
end
exec sp_task1 #table = 'table1'
This is what I get in result when I Execute the SP.
+----+
| id |
+----+
| 1 |
| 2 |
+----+
+------+
| name |
+------+
| A |
+------+
+---------+
| Address |
+---------+
| Max |
| Min |
+---------+
+-----------+
| city |
+-----------+
| Rajasthan |
| Delhi |
+-----------+
Now, I want to do this dynamically without specifying the column names.
Please give me any kind of help regarding this issue.
You can use below query to do this.
TEST SETUP
CREATE TABLE Test(id int, name varchar(20), city varchar(20));
INSERT INTO Test
values(1,'abc','chennai'),
(2,'abc','bangalore');
CREATE PROCEDURE USP_GetDistinct(#TableName SYSNAME,#TableSchema SYSNAME )
AS
BEGIN
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = (
SELECT 'SELECT DISTINCT ' +
Column_Name +
' FROM ' + QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(Table_Name) + CHAR(13)+ CHAR(10) + 'GO'+CHAR(13)+ CHAR(10)
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE TABLE_NAME = #TableName AND table_schema = #TableSchema
FOR XML PATH(''), Type
).value('.', 'varchar(max)')
--PRINT #SQL
EXECUTE (#SQL)
END
Execute procedure
EXEC USP_GetDistinct #TableName='Test', #TableSchema='dbo'
Result set
+----+
| id |
+----+
| 1 |
| 2 |
+----+
+------+
| name |
+------+
| abc |
+------+
+-----------+
| city |
+-----------+
| bangalore |
| chennai |
+-----------+

How to use subquery in openquery where clause

So I have this Openquery in a stored procedure, where I need to return results where the values in a column are the same as the ones in a local table
exec spx_SELECT_LocalizacoesEtiquetas
GO
IF OBJECT_ID('dbo.spx_SELECT_LocalizacoesEtiquetas') IS NOT NULL
DROP PROCEDURE spx_SELECT_LocalizacoesEtiquetas
GO
CREATE PROCEDURE spx_SELECT_LocalizacoesEtiquetas
AS
BEGIN
SET NOCOUNT ON
DECLARE #SQL NVARCHAR(MAX);
SET #SQL =
'SELECT ET0109 AS Localizacao, Etiquetas
FROM OpenQuery(MACPAC, ''SELECT FET001.ET0109, FET001.ET0101 AS Etiquetas
FROM AUTO.D805DATPOR.FET001 FET001
WHERE FET001.ET0104=''''POE'''' AND FET001.ET0105=''''DIS'''''' AND FET001.ET0101 = '''''
+ (SELECT Localizacao FROM xLocalizacao WHERE InventarioID = 1 ) + ''''' ) ';
EXEC sp_executesql #SQL
END
basically it won't accept the subquery 'cause it says it has too many values.... So my question is. How can i limit the values from the subquery where the values of a column match the ones in a local table? basically a where column A in open query = column B in local table
EDIT.
Here is what I'm trying to achieve.
SubQuery returns from Local table
Column A
| A |
| B |
| C |
| D |
| E |
Open query returns
Column A Column B
| A | 0 |
| A | 0 |
| A1 | 1 |
| A | 2 |
| B | 3 |
| B | 3 |
| B1 | 4 |
Final result should Be
Final query
Column A Column B
| A | 0 |
| A | 0 |
| A | 2 |
| B | 3 |
| B | 3 |
Ok, there are two changes you need to make in your approach.
First of all, you are concatenating your sub-query to a string. No matter what, your subquery has to return a single value, not a multi-row set. So you need to use the method of your choice for having your query return a comma-separated string.
Here's one that will work on any version of SQL Server after 2005.
in other words, instead of this:
Column A
| A |
| B |
| C |
| D |
| E |
your subquery needs to return a single varchar column containing this:
'A','B','C','D','E'
The next change you need to make is using IN instead of =.
So instead of this:
AND FET001.ET0101 = '''''
+ (Your Subquery) + ''''' ) '
you need this:
AND FET001.ET0101 IN ( '
+ (Your Subquery) + ') ) '

Pivot Rows to Column By Group in SQL Server

I am looking for a code which can transpose row to column using the group by in the SQL Server 2008.
This is my table :
Name | Stdy | Val
-------+---------+-------
Kunjan | Technic | 80
Kunjan | Sains | 90
Kunjan | Sport | 60
Shone | Technic | 60
Shone | Sains | 80
Shone | Sport | 70
Peudd | Technic | 85
Peudd | Sains | 75
Peudd | Sport | 90
What I want to eventually display is something like this (for the data above):
Stdy | Kunjan | Shone | Peudd
--------+--------+-------+-------
Technic | 80 | 60 | 85
Sains | 90 | 80 | 75
Sport | 60 | 70 | 90
Any help would be appreciated.
Thanks in advance.
this can be done using PIVOT operator
select *
from yourtable
pivot
(
max(Val)
for Name in ([Kunjan], [Shone], [Peudd])
) p
or conditional CASE statement. There are lots of example, just do a search on it
EDIT : for dynamic case
declare #sql nvarchar(max),
#col nvarchar(max)
select #col = isnull(#col + ',', '') + Name
from yourtable
group by Name
print #col
select #sql =
'
select *
from youtable
pivot
(
max(Val)
for Name in (' + #col + ')
) p
'
print #sql
exec sp_executesql #sql

Dynamic Sql Pivot Two Columns of Table

Using Dynamic Pivots Tables, I'm trying to get this table: http://www.sqlfiddle.com/#!18/9f1cf/47
To look something like this: (Some Columns removed for brevity, assume I can have one or more columns past "Chosen Council", this is the expected design only not the expected result)
Note that Zip codes can be null, can share Councils, and can repeat over the days
+============+=======+================+============================+====================================+=========================+=====================+
| Call Date | Zip | Chosen Council | Early Childhood Group Care | Development / Developmental Delays | Caregiver Mental Health | Behavioral Concerns |
+============+=======+================+============================+====================================+=========================+=====================+
| 2018-05-01 | 85000 | Maricopa North | null | 1 | 2 | null |
+------------+-------+----------------+----------------------------+------------------------------------+-------------------------+---------------------+
| 2018-05-01 | 85001 | Maricopa North | 1 | null | null | null |
+------------+-------+----------------+----------------------------+------------------------------------+-------------------------+---------------------+
| 2018-05-01 | null | null | null | 2 | null | null |
+------------+-------+----------------+----------------------------+------------------------------------+-------------------------+---------------------+
| 2018-05-02 | 85000 | Maricopa North | null | 1 | 1 | 3 |
+------------+-------+----------------+----------------------------+------------------------------------+-------------------------+---------------------+
| 2018-05-02 | 85003 | Phoenix South | null | null | null | 2 |
+------------+-------+----------------+----------------------------+------------------------------------+-------------------------+---------------------+
| 2018-05-02 | 85004 | Phoenix South | 1 | 2 | null | 2 |
+------------+-------+----------------+----------------------------+------------------------------------+-------------------------+---------------------+
| 2018-05-02 | null | null | null | 1 | 1 | null |
+------------+-------+----------------+----------------------------+------------------------------------+-------------------------+---------------------+
I've seen a number of questions regarding Pivot Tables, both hard coded and dynamic, and I'm still not grasping it.
Here, I was able to get a Dynamic Pivot Table for just the Call Topic Names and their Counts: http://www.sqlfiddle.com/#!18/9f1cf/39
But that is only a single row for everything, it also seems to be ignoring nulls.
Here I tried to expand on the above, and while it seems to be spacing out better, I haven't figured out how to attach my Call Date, Zip, or Chosen Council columns: http://www.sqlfiddle.com/#!18/9f1cf/37
Any ideas how I can do this?
ASCII Table made with: Made with https://ozh.github.io/ascii-tables/
Maybe you need something like below
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns += N', _callTopics.' + QUOTENAME(Name)
FROM
(
SELECT
_callTopics.Name
FROM CallTopics AS _callTopics
INNER JOIN CallTopicsPerRegion AS _callTopicsPerRegion
ON _callTopics.Name = _callTopicsPerRegion.CallTopicName
GROUP BY _callTopics.Name
) AS x;
SET #sql = N'
SELECT CallDate
,Zip
,ChosenCouncil, ' + STUFF(#columns, 1, 2, '') + '
FROM
(
SELECT _callTopics.Name, _callTopicsPerRegion.CallTopicCount,
CallDate
,Zip
,ChosenCouncil
FROM CallTopics AS _callTopics
INNER JOIN CallTopicsPerRegion AS _callTopicsPerRegion
ON _callTopics.Name = _callTopicsPerRegion.CallTopicName
) AS j
PIVOT
(
SUM(CallTopicCount) FOR Name IN ('
+ STUFF(REPLACE(#columns, ', _callTopics.[', ',['), 1, 1, '')
+ ')
) AS _callTopics order by 1,2 ,3';
--PRINT #sql;
EXEC sp_executesql #sql;
Here's fiddle link

make a combination (variations) of records on the basis of a dynamic column value (attributes) using sql

I have a table which contains Attributes
AttrId Title
1 | Color
2 | Size
3 | Style
Another Table which contains Variants
Id | ProductId | AttrId | Value
1 | 1 | 1 | Red
2 | 1 | 1 | Green
3 | 1 | 1 | Blue
4 | 1 | 2 | Small
5 | 1 | 2 | Medium
6 | 1 | 2 | Large
7 | 1 | 3 | New
8 | 1 | 3 | Used
Now i want to make all possible combination related to Attributes
Output should be look like this:
Size | Color | Style
Small | Red | New
Small | Red | Old
Small | Green | New
Small | Green | Old
Small | Blue | New
Small | Blue | Old
Medium | Red | New
Medium | Red | Old
Medium | Green | New
Medium | Green | Old
Medium | Blue | New
Medium | Blue | Old
Large | Red | New
Large | Red | Old
Large | Green | New
Large | Green | Old
Large | Blue | New
Large | Blue | Old
So, there's 18 possible Combinations (Variations) with Attributes
Attributes and Variants can be dynamic
How is this possible in SQL Server Database Query?
SQL DEMO
WITH Color as (
SELECT V.[Value] as Color
FROM Attributes A
JOIN Variants V
ON A.[AttrId]= V.[AttrId]
WHERE A.[AttrId] = 1
), Size as (
SELECT V.[Value] as Size
FROM Attributes A
JOIN Variants V
ON A.[AttrId]= V.[AttrId]
WHERE A.[AttrId] = 2
), Style as (
SELECT V.[Value] as Style
FROM Attributes A
JOIN Variants V
ON A.[AttrId]= V.[AttrId]
WHERE A.[AttrId] = 3
)
SELECT *
FROM Color
CROSS APPLY Size
CROSS APPLY Style;
OUTPUT
Here is doing a dynamic sql. You loop from the Attributes table and create a cte and a cross join for each one. Then execute the dynamic sql. You will get the same result.
SQL DEMO:
CREATE PROCEDURE GetCombinations(#TableName varchar(20))
AS
BEGIN
DECLARE #sql varchar(4000);
DECLARE #cte varchar(4000);
DECLARE #select varchar(4000);
DECLARE Cur CURSOR FOR
SELECT [AttrId], [Title] FROM Attributes;
OPEN Cur;
SELECT #cte = 'WITH ';
SELECT #select = 'SELECT * FROM ';
DECLARE #AttrId VARCHAR(255);
DECLARE #Title VARCHAR(255);
FETCH NEXT FROM Cur INTO #AttrId, #Title;
WHILE ( ##FETCH_STATUS = 0 )
BEGIN
SELECT #cte = #cte + #Title + ' as (' +
'SELECT V.[Value] as ' + #Title + ' ' +
'FROM Attributes A ' +
'JOIN Variants V ' +
'ON A.[AttrId]= V.[AttrId] ' +
'WHERE A.[AttrId] = ' + #AttrId +
'), ';
SELECT #select = #select + #Title + ' CROSS APPLY ';
FETCH NEXT FROM Cur INTO #AttrId, #Title;
END
SELECT #cte = LEFT(#cte, LEN(#cte) - 1);
SELECT #select = LEFT(#select, LEN(#select) - 12);
-- for debug --
SELECT #sql= 'SELECT ''' + #cte + #select + ''' as title';
EXEC (#cte + #select);
END;

Resources