SQL Query to Fetch only column with value equals to current date - sql

Need SQL logic
I have table with Column name EID, Emp_name, 1,2,3,4,5,6,7....till 31 (Column 1 to 31 are calendar dates)
now i m trying to fetch only 3 column EID, EMP_name and date which is equals to sys date.
Example
Todays SYS_date is 2nd-Jan-2019
and i need column with value = 2 like this...
EID Emp_name 2 |
123 James SCOTT P |
133 Mark M A |
133 Mark Man P |

Try like this:
declare #sql nvarchar(max)
set #sql = 'select EID, Emp_name, [' + convert(nvarchar(2),day(getdate())) + '] as d from tableName '
exec(#sql)

One method to approach this is to unpivot the data. Here is one method:
select t.eid, t.emp_name, v.n
from t cross apply
(values ([1]), ([2]), ([3]), . . ., ([4])
) v(n)
where v.n = day(getdate());
I will caution that the column name is a fixed name, not 2. SQL queries have fixed column names. You would need to use dynamic sql to get a variable column name.

Related

Creating a distinct list of names with dynamic columns

I will try and keep this as concise and easy to understand as possible. I have a dataset which includes a large number of names, some are distinct, however some are not and all names have a corresponding reference number. Where the names are not distinct, I want to create a query that will display a distinct list all of names in that table, and have seperate columns that list listing the reference numbers of the names in the original dataset. Is this at all possible using SQL? I was thinking a PIVOT clause might be required, but not sure that would be appropriate
Like below;
Current Dataset
FullName
Reference
Joe Bloggs
T1234567
Joe Bloggs
T3456789
John Smith
T1234568
Sarah Edwards
T1234567
Karen Culford
T0999221
Sarah Edwards
T0239222
Joe Bloggs
T2045292
Desired Outcome
FullName
Reference1
Reference2
Reference3
Joe Bloggs
T1234567
T3456789
T2045292
John Smith
T1234568
NULL
NULL
Sarah Edwards
T1234567
T0239222
NULL
Karen Culford
T0999221
NULL
NULL
If the number of pivot columns is unknown, you'd need dynamic sql (which has both pros and cons). Using this example as a base, first build a comma separated list of column names "Reference1,Reference2,....".
SQL Server 2017+
DECLARE #colList AS NVARCHAR(MAX)
, #query AS NVARCHAR(MAX);
; WITH colsByName AS (
-- count how many columns per fullName
SELECT FullName
, Reference
, ROW_NUMBER() over(PARTITION BY Fullname ORDER BY Reference) AS ColNum
FROM YourTable
)
, uniqueColumns AS
(
-- get unique column numbers
SELECT DISTINCT ColNum
FROM colsByName
)
-- build comma separated list of names
SELECT #colList = STRING_AGG('Reference'+ CONVERT(VARCHAR, ColNum), ',')
FROM uniqueColumns
;
Note, for SQL Server 2016 use STUFF instead of STRING_AGG
...
-- build comma separated list of names
SELECT #colList = STUFF((
SELECT ',' + 'Reference'+ CONVERT(VARCHAR, ColNum)
FROM uniqueColumns
ORDER BY ColNum
FOR XML PATH('')
)
,1,1,'')
;
Then use it to build a dynamic PIVOT statement:
SET #query = 'SELECT FullName, ' + #colList + '
FROM (
SELECT FullName
, Reference
, ''Reference''+ CONVERT(VARCHAR, ROW_NUMBER() over(PARTITION BY Fullname ORDER BY Reference)) AS ColNum
FROM YourTable
) x
PIVOT
(
MAX(Reference)
FOR ColNum IN (' + #colList + ')
) p ';
EXECUTE(#query);
See also
SQL Server 2017+ Example - db<>fiddle
SQL Server 2016 Example - db<>fiddle
Results
FullName
Reference1
Reference2
Reference3
Joe Bloggs
3456789
T1234567
T2045292
John Hart
John Smith
T1234568
Karen Culford
T0999221
Sarah Edwards
T0239222
T1234567

SQL - How to use Comma separated column values in a where clause

I have a table called Configuration. It contains the values like below,
Id SourceColumns TargetColumns SourceTable TargetTable
1 Name, Age CName, CAge STable TTable
2 EId EmplId EmpTable TTable
In a stored procedure, I have to get the column names from the above table and I have to compare the source table and target table.
I am able to do that easily for the 2nd record as it has only one column name, so in the where clause I can write sourcecolumn = targetcolumn like,
SELECT
EId
, EmplId
FROM
EmpTable E
JOIN TTable T ON E.Eid = T.EmplId
The first record in the table has 2 columns separated by comma (,).
I have to compare like this,
SELECT
Name
, Age
FROM
STable S
JOIN TTable T ON S.Name = T.CName AND S.Age = T.CAge
In some cases the source columns and target columns may have more column names separated by comma(,)
Please help me on this.
As I don't know whether you have completely understood the data model I suggested in the request comments and in order to properly answer the question:
Your table is not normalized, as the data in the columns SourceColumns and TargetColumns is not atomic. And one even has to interpret the data (the separator is the comma and the nth element in one column relates to the nth element in the other column).
This is how your tables should look like instead (the create statements are pseudo code):
create table configuration_tables
(
id_configuration_tables int,
source_table text,
target_table text,
primary key (id_configuration_tables),
unique key (source_table),
unique key (target_table) -- or not? in your sample two souce table map to the same target table
);
create table configuration_columns
(
id_configuration_columns int,
id_configuration_tables int,
source_column text,
target_column text,
primary key (id_configuration_columns),
foreign key (id_configuration_tables) references configuration_tables (id_configuration_tables)
);
Your sample data would then become
configuration_tables
id_configuration_tables | source_table | target_table
------------------------+--------------+-------------
1 | STable | TTable
2 | EmpTable | TTable
configuration_columns
id_configuration_columns | id_configuration_tables | source_column | target_column
-------------------------+-------------------------+---------------+--------------
1 | 1 | Name | CName
2 | 1 | Age | CAge
3 | 2 | EId | EmplId
As of SQL Server 2017 you can use STRING_AGG to create your queries is. In earlier versions this was also possible with some STRING_AGG emulation you will easily find wit Google or SO.
select
'select s.' + string_agg (c.source_column + ', t.' + c.target_column, ', ') +
' from ' + t.source_table + ' s' +
' join ' + t.target_table + ' t' +
' on ' + string_agg('t.' + c.target_column + ' = s.' + c.source_column, ' and ') +
';' as query
from configuration_tables t
join configuration_columns c on c.id_configuration_tables = t.id_configuration_tables
group by t.source_table, t.target_table
order by t.source_table, t.target_table;
Demo: https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=8866b2485ba9bba92c2391c67bb8cae0

Select data from one table & then rename the columns based on another table in SQL server

I have two tables. TableOne which contains two columns (name & value). TableTwo can contain N no. of columns. Number of rows in TableOne will be equal to number of columns in TableTwo.
see the below image for more information.
What I want:
When I run select query on TableTwo, the result-set should pick the column names based on value column of TableOne. We need to match column name of TableTwo with rows available in TableOne and perform transform.
So the output should look like this:
ColumnOne | ColumnTwo | columnThree | ColumnFour
1 1 1 2015-05-08 15:28:22.630
2 2 2 2015-05-07 15:28:22.630
................
................
You can use dynamic sql to generate the query to execute based on the values in the first table. Here is an example:
Declare #dynamicSQL nvarchar(200)
SET #dynamicSQL = 'SELECT ' + (SELECT stuff((select ',' + name + ' AS ' + value
from Table1
for xml path('')),1,1,'')) + ' FROM Table2'
EXECUTE sp_executesql #dynamicSQL
SQL Fiddle: http://sqlfiddle.com/#!6/768f9/10

Add character to selected duplicate value in SQL

I need to add character / number to selected duplicate values.
This is what I need:
SELECT Name -- Here I need to add for example 1 if It have duplicates
-- If It is hard way to code, how to add 1 to all selected values?
FROM Example
WHERE Id BETWEEN 25 AND 285
If there are 2 equal names Peter It should select Peter and second Peter1
If there is no easy way to make It, how to add 1 to all selected lines? Should select Peter1 instead of Peter
I've tried this:
SELECT Name + ' 1' AS Name -- in this case selecting wrong column
FROM Example
WHERE Id BETWEEN 25 AND 285
EDIT
SELECT #cols += ([Name]) + ','
FROM (SELECT Name --I neeed to integrate It here
FROM FormFields
WHERE ID BETWEEN 50 AND 82
) a
If I use this:
SELECT #cols += ([Name]) + ',' -- here throws error
FROM (SELECT Name + CASE WHEN RowNum = 1 THEN '' ELSE CONVERT(NVARCHAR(100), RowNum-1) END AS [UpdatedName]
FROM (
SELECT Name AS Name,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Name) AS "RowNum"
FROM FormFields
WHERE Id Between 50 And 82) x
) a
It throws error: Invalid column name 'Name'.
EDIT 2
It's different tests but some of them have the same criteria. That's why I need It to rename.
You can do this via getting the Row_Number and using a Case. Here's an example for SQL Server:
;With Cte As
(
Select Name, Row_Number() Over (Partition By Name Order By Name) RN
From Example
Where Id Between 25 And 285
)
Select Case When RN = 1 Then Name Else Name + Cast((RN - 1) As Varchar (3)) End As Name
From Cte
You could use the ROW_NUMBER function built into SQL server.
select Name + case when RowNum = 1 then '' else CONVERT(varchar(100), RowNum-1) end as "UpdatedName"
from (
select name as "Name",
ROW_NUMBER() over (partition by name order by name) as "RowNum"
from Example
Where Id Between 25 And 285) x
Please note that this still doesn't guarantee you unique names. Afterall, someone could already have a name of "MyName1", so if you had 2 people with names "MyName" you'd still get 2 "MyName1" with this select statement.
This is very unusual request, it looks like you are trying to "make car run with wheels on the roof" :)
The root problem is almost sure wrong database design... Pivot is usually used for data summaries. If you have in the same column "Peter" and "Peter" with different meanings, it looks that there is something wrong. Or do you need to differentiate both Peters for any other reason?
I do not understand what are you trying to achieve. If Peter is always Peter, and you just want to avoid duplicities, you can simply use "group by Name". But this is what pivot does automatically... If Peter and Peter have two different meanings (like Peter1 and Peter2), you should think about changing database structure, if possible.
Or try to explain more deeply what are you trying to achieve.
EDIT:
OK, now I understand the desired output. And what is the structure of your source data table(s)? From your schema it is clear that you need to make PIVOT columns based on
Testname+groupId
or
Testname+convert(varchar(100),groupId)
if groupId is number. That is your Peter1,Peter2 composition. It will create columns that you need. But I dont't know where testname and groupId are located in your datatables. Do test names correspond to column NAMES or to VALUES stored in DB? Is groupId something like TestId? Again column or value? Provide more info about source data structure, if you need more help, your problem is not so complicated.
Since the columns have group IDs, concatenate the Column name with an Underscore and GroupID as a key value and when you display it, strip the underscore and trailing characters.
Like This:
SELECT #cols += ([Name]) + ','
FROM (SELECT Name + '_' + CAST(GroupId AS varchar)
FROM FormFields
WHERE ID BETWEEN 50 AND 82
) a
I assume you are using this to build a dynamic SQL statement. I'm not sure what the schema of your FormFields Table is, but if it includes something like the test name you could append an AS [Name] + ' - ' +[TestName] to have the column header be something more useful. I would say try a PIVOT, but that could get pretty ungainly if the tests don't have the majority of the fields in common...
I also assume you are storing responses to these prompts in a table that looks something like this:
CREATE TABLE [Responses]
(
RespID int IDENTITY NOT NULL,
UserID int NOT NULL,
FieldID int NOT NULL,
RespVal int/varchar/whatever NOT NULL
)
Then perhaps you have a [Test] table with some test metadata that acts as the primary key for your GroupID Foreign key in your FormFields table.
In your example you show responses across all columns, but I'm not sure how that would work since (unless I'm missing something in your explanation and the inferences I've made to your design) one set of responses would only be populated for one of the groups per row, unless you are aggregating responses, but then by what criteria? Perhaps the rows correspond to respondents and all respondents are required to answer across all form types. In that case, your output would work as a PIVOT like this:
DECLARE #sql varchar(4000) = ''
DECLARE #colList varchar(1000)
DECLARE #selList varchar(1000)
;WITH NameBase
AS
(
SELECT t.Name [TestName], f.Name [FieldName], f.GroupId
FROM [FormFields] f
INNER JOIN [Tests] t ON f.GroupID = t.ID
)
SELECT #colList = COALESCE(#colList + ',','') + QUOTENAME([FieldName] + '_' + [GroupId])
, #selList = COALESCE(#selList + ',','') + QUOTENAME([FieldName] + '_' + [GroupId]) + ' AS ' + QUOTENAME([FieldName] + ' - ' + [TestName])
FROM NameBase
SELECT #sql = 'SELECT [UserName],' + #selList + ' FROM (
SELECT u.Name [UserName], f.Name + '_' + f.GroupId [FieldName], r.RespVal [Response]
FROM Responses r
INNER JOIN [TestUsers] u ON r.UserID = u.ID
INNER JOIN [FormFields] f ON r.FieldID = f.ID) t
PIVOT (MAX([Response]) FOR [FieldName] IN (' + #colList + ')) pvt'
EXECUTE(#sql);
I haven't tested that yet, but it should at least point you in the right direction. I'll try to build a SqlFiddle to test it in a little bit.

How to change row into column?

How can I change this table
Name subject Mark
Aswin physics 100
Aswin chemistry 300
Aswin maths 200
Into
Aswin Physics 100 Chemistry 300 Maths 200
Any one please help me.
you can use PIVOT operator to do this job in sql server.
check these links link1 and link2 they will show how to change row into column.
hope this helps you!
SQLFiddle demo
select Name,
sum(CASE
when [subject]='physics' then Mark
end) as Physics,
sum(CASE
when [subject]='chemistry' then Mark
end) as chemistry,
sum(CASE
when [subject]='maths' then Mark
end) as maths
from t group by Name
Or if you need it in one line:
SQLFiddle demo
SELECT
t1.name,
MemberList = substring((SELECT ( ', ' + subject+' - '+
cast(Mark as varchar(100)) )
FROM t t2
WHERE t1.name = t2.name
ORDER BY
name,
subject
FOR XML PATH( '' )
), 3, 1000 )FROM t t1
GROUP BY name
You need to use SQL Pivoting, check the examples at SQL SERVER – PIVOT and UNPIVOT Table Examples. Using Sql Pivoting you can change the rows to columns and Unpivoting is for columns to rows conversion.
Please note: I am checking if I can provide you exact script but for now the link would help you out.
UPDATE
Code example
Though I have not tested this with actual data but it parses fine.
-- Pivot Table ordered by Name of Student
SELECT Name, Physics, Chemistry, Maths
FROM (
SELECT Name, Subject, Mark
FROM Student) up
PIVOT (SUM(Mark) FOR Student IN (Physics, Chemistry, Maths)) AS pvt
ORDER BY Name
-- Result should be something like
----------------------------------
Name Physics Chemistry Maths
----------------------------------
Aswin 100 300 200
----------------------------------
For creating pivot you need to know the actual rows values to convert into columns.
I have wrote before about dynamic pivoting here if you find it useful.
It is not exactly clear if you want this data in separate columns or in one column.
If you want this in separate columns, then you can apply the PIVOT function which became available in SQL Server 2005.
If you know all of the values that you want to transform or have a limited number, then you can hard-code the query:
select *
from
(
select name, subject +' '+ cast(mark as varchar(9)) as sub_mark,
'Subject_'+cast(row_number() over(partition by name
order by subject) as varchar(10)) col_name
from subjects
) s
pivot
(
max(sub_mark)
for col_name in (Subject_1, Subject_2, Subject_3)
) piv;
See SQL Fiddle with Demo. You will notice that I did this slightly different from the other pivot answer. I placed both the subject/mark in the same column with a column name of Subject_1, etc.
If you have an unknown number of values, then you can use dynamic sql:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME('Subject_'+cast(row_number() over(partition by name
order by subject) as varchar(10)))
from subjects
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT name,' + #cols + ' from
(
select name, subject +'' ''+ cast(mark as varchar(9)) as sub_mark,
''Subject_''+cast(row_number() over(partition by name
order by subject) as varchar(10)) col_name
from subjects
) x
pivot
(
max(sub_mark)
for col_name in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo. The dynamic sql version will increase in the number of columns if a name has more than 3 subjects.
The result of both queries is:
| NAME | SUBJECT_1 | SUBJECT_2 | SUBJECT_3 |
---------------------------------------------------
| Aswin | chemistry 300 | maths 200 | physics 100 |