Dynamic SQL in SQL Server - how to? - sql-server

My question is about dynamic SQL. I have two tables
customers (custid, fn_name, Ln_name)
vendors (vendor_id, Custid, ordernum, orderdate)
And also I have other tables
table (tableid(pk), name)
table_col (colid(pk), tableid(fk), colname)
table_keys (key_id, tableid, key_col_name)
Now if user selects any column from the table_col tables, dynamically I need to get column names and dynamically identify the join from the table_keys based on the name match.

Could be somethings like this
select
t.name
, c.colname
, case k.key_col_name when not null then 'X' else '' end as iskey
from table_col as c
left join table_keys as k on k.key_col_name = c.colname
inner join table as t on t.tableid = c.tableid
where t.name in ('customers', 'vendors');

Related

Accessing a new table created after a join

I have joined select columns from 3 tables into a new table using the following:
SELECT A.ExternalID, A.UserDefinedXml.value('(Skin_Sheet/#Label)[1]', 'varchar(3)') AS SS, A.ServiceSiteUid, A.LastModifiedDate, A.PersonUid,
B.FirstName, B.LastName, B.PersonUid,
C.Name
FROM Patient A
INNER JOIN Person B ON B.PersonUid = A.PersonUid
INNER JOIN ListServiceSite C ON C.ServiceSiteUid = A.ServiceSiteUid
WHERE SS IS NOT NULL
ORDER By LastModifiedDate;
This all works but I'm not sure how to reference the column SS created from data extracted from the XML so I can only select the observations in which the value is "Yes" or "No". In R I would have created a new object but I'm not sure how SQL stores this new table if I don't specify what the table name is.
Side note, I did try to insert this into a new table but SQL wasn't letting me because, for some reason, the join resulted in PersonUid being duplicated.
Thank you in advance, I'm very, very new to SQL and trying to learn on the fly.
Conceptually WHERE comes before SELECT, so you need to push the query into a derived table subquery or Common Table Expression (CTE) to reference SS in a WHERE clause. EG
with q as
(
SELECT A.ExternalID, A.UserDefinedXml.value('(Skin_Sheet/#Label)[1]', 'varchar(3)') AS SS, A.ServiceSiteUid, A.LastModifiedDate, A.PersonUid,
B.FirstName, B.LastName, B.PersonUid,
C.Name
FROM Patient A
INNER JOIN Person B ON B.PersonUid = A.PersonUid
INNER JOIN ListServiceSite C ON C.ServiceSiteUid = A.ServiceSiteUid
)
SELECT *
FROM q
WHERE SS IS NOT NULL
ORDER By LastModifiedDate;
This will put your results into a temp table, and avoids the problem of having two columns with the same name:
SELECT
A.ExternalID
,SS = A.UserDefinedXml.value('(Skin_Sheet/#Label)[1]', 'varchar(3)')
,A.ServiceSiteUid
,A.LastModifiedDate
,PersonUid_A = A.PersonUid
,B.FirstName
,B.LastName
,PersonUid_B = B.PersonUid
,C.Name
INTO #TempResults
FROM Patient A
INNER JOIN Person B ON B.PersonUid = A.PersonUid
INNER JOIN ListServiceSite C ON C.ServiceSiteUid = A.ServiceSiteUid
WHERE SS IS NOT NULL
ORDER BY LastModifiedDate;

How to SUM() appropriate rows in MSSQL query for one column with INNER JOIN?

I have a MSSQL query:
SELECT Artikel.ArtikelID, Artikel.K_HerstellerName AS ManufacturerName, Artikel.K_HerstellerPN AS ManufacturerPN, Artikel.ArtikelNummer AS SupplierPN, Artikel.Bezeichnung + ' - ' + Artikel.LangText AS Description,
Artikel.LetzterEK AS Price, Artikel.IstGesperrt AS Gesperrt, Lager.Verfuegbar AS Quantity, Lager.LagerPlatz AS LP
FROM Artikel INNER JOIN
Lager ON Artikel.ArtikelID = Lager.ArtikelID
WHERE (Artikel.K_HerstellerPN <> '') AND (Artikel.IstGesperrt = 'False') AND (Artikel.ArtikelNummer LIKE '%[0-9]-[0-9]%')
Here you can see how it looks like: https://ibb.co/fyOHv9
How to SUM() appropriate rows in MSSQL query for column "Quantity"?
In result all rows must be unique.
you need to group by all the columns that make each row unique, then use aggregate functions in your select, for example:
SELECT
MIN(Artikel.ArtikelID ) ArtikelID,
MIN(Artikel.K_HerstellerName) ManufacturerName,
SUM(Lager.Verfuegbar) Quantity
FROM Artikel
INNER JOIN Lager ON Artikel.ArtikelID = Lager.ArtikelID
WHERE (Artikel.K_HerstellerPN <> '') ...
GROUP BY
Artikel.ArtikelID,
Artikel.K_HerstellerName,
...

SQL boolean EXISTS while using aggregate function

I have written a query that returns the list of all customers that have ever made a purchase with the company I work for. The person for whom I am getting the data would like to know if a specific criteria is true for any of these orders.
select L.ParentLocation,
[Number of Orders] = count(distinct(T.Order))
from Table1 L
join Table2 T
on L.Location = T.Location
group by L.ParentLocation
However, the issue is complicated because I am already grouping by ParentLocation, and each ParentLocation has many normal Locations. So I am counting the number of unique orders at the location level, then grouping them by the ParentLocation.
I want to return 'TRUE' in the query if a field 'OrderDesc' contains "Toys" in ANY of the orders by ANY of the Locations owned by a ParentLocation. Is there a way to do this?
NOTE: Table2 contains the OrderDesc column.
Thanks for reading!
select
L.ParentLocation,
[Number of Orders] = count(distinct(T.Order)),
has_toys = max(case when t.OrderDesc like '%toys%' then 'TRUE' else '' end)
from Table1 L
inner join Table2 T
on L.Location = T.Location
group by
L.ParentLocation

three tables join is not giving desired output

I have three tables:
1: Station_Details (master data table)
2: RF_Details
3: WL_Details
As mention in below image.
I need a to take data from all three table in to a output table
Master data from Station_details and other data from RF and WL tables.
If RF_Details and WL_Details tables are having same station id and same DateTime then in output table both rows details will show in one row.
If DateTime are different then it will appear in different rows.
I tried this sql query but I am not getting the same output like OUTPUT Table.
select rf.StationID, st.stationname, st.state,rf.rf,rf.cum-rf,wl.wl,DataTime
from [RF_Details] rf
join [WL_Details] wl
join Station_Details st
on rf.StationID = wl.StationId and
rf.DataRecieved=wl.DataRecieved and
st.stationid =rf.stationid and
st.stationid = wl.stationid;
But it didn't give the right number of rows and output.
Please help me for the same.
You should always put the join conditions along with the join itself. Also, adding the INNER is a practice I follow to ensure no extra records are returned.
SELECT rf.StationID, st.stationname, st.state, wl.DataRecieved, wl.waterlevel1,
rf.dailyrainfall, rf.cumrainfall
FROM [RF_Details] rf
INNER JOIN [WL_Details] wl
ON rf.StationID = wl.StationId AND
rf.DataRecieved=wl.DataRecieved
INNER JOIN Station_Details st
ON st.stationid =rf.stationid AND
st.stationid = wl.stationid;
declare #station_details table(id int, station_id varchar(10),station_name varchar(10),state varchar(10))
declare #rf_details table (id int, station_id varchar(10),rf int, cum_rf int, dt dateTIME)
declare #wl_details table (id int, station_id varchar(10),wl int,dt datetime)
insert into #station_details values
(1,'DEL-NDL','NDL','DEL'),
(2,'UP-LKO','LKO','UP'),
(3,'MP-BHP','BHP','MP'),
(4,'MHR-MUM','MUM','MHR')
INSERT INTO #RF_DETAILS VALUES
(1,'DEL-NDL',42,435,'2016-06-13 05:15:00'),
(2,'UP-LKO',0,501,'2016-06-13 05:15:00'),
(3,'MP-BHP',20,350,'2016-06-13 05:15:00'),
(4,'MHR-MUM',30,200,'2016-06-13 05:15:00'),
(5,'MHR-MUM',15,100,'2016-06-14 05:15:00'),
(6,'UP-LKO',50,350,'2016-06-13 05:15:00')
INSERT INTO #WL_DETAILS VALUES
(1,'DEL-NDL',25,'2016-06-13 05:15:00'),
(2,'UP-LKO',35,'2016-06-13 05:30:00'),
(3,'MP-BHP',46,'2016-06-13 05:45:00'),
(4,'MHR-MUM',20,'2016-06-13 05:15:00'),
(5,'MHR-MUM',15,'2016-06-14 05:15:00'),
(6,'UP-LKO',60,'2016-06-13 05:15:00')
;with cte as
(
SELECT case
when rf.dt = wl.dt then 'Y'
else 'N'
end as matched,
rf.id as id,rf.station_id as stationid,rf.rf as rf , rf.cum_rf as cumrf , rf.dt as rfdt,
wl.id as wlid, wl.station_id ,wl.wl ,wl.dt as wldte,
rf.station_id as station,rf.dt as rfdte
FROM #RF_DETAILS RF
JOIN #WL_DETAILS WL ON rf.id = wl.id and RF.STATION_ID = WL.STATION_ID
)
select row_number() over (order by s.id) newid,
s.id,s.station_id,sd.station_name,sd.state,s.rf,s.cumrf,s.wl,
case
when s.srce = 'L' then s.rfdte
else s.wldte
end as 'Date'
from
(
select 'L' as srce,cte.id,cte.station_id,cte.rf,cte.cumrf, cte.wl as wl, cte.rfdte,cte.wldte from cte where cte.matched = 'Y'
union
select 'L' as srce,cte.id,cte.station_id,cte.rf,cte.cumrf, null as wl, cte.rfdte,cte.wldte from cte where cte.matched = 'N'
union all
select 'R' as srce,cte.id * 10,cte.station_id,null,null, cte.wl as wl, cte.rfdte,cte.wldte from cte where cte.matched = 'N'
) s
join #station_details sd on sd.station_id = s.station_id
order by s.id
You should redesign your database: right now you have a secondary key in RF_Details and WL_Details - DateTime. Which plays a role of a foreign key between them. Which is no good and will continue confusing you every time you need to join those table or collect corresponding data.
There should be another table like Station_Records which will store a row per every record for that station: (id, station_id, record_date_time). RF and WL rows if any should refer this table instead of referring Station_Details with station_id and one another with datetime.
With current structure you need to do full join of RF and WL to get both: matching by datetime - in same row, not matching - in separate rows.
select sd.station_name, Station_Records.*
from Station_Details sd
inner join
(
select
IsNull(rf.station_id, wl.station_id) station_id,
IsNull(rf.DataRecieved, wl.DataRecieved) DataRecieved,
rf.rf, rf.cum-rf, wl.wl
from [RF_Details] rf
full join [WL_Details] wl
on wl.station_id = rf.station_id
and wl.DataRecieved = rf.DataRecieved
) Station_Records
on Station_Records.station_ud = sd.station_id
concrete implementation may consist of OUTER APPLY or even be without any subqueries - it does not really matter currently.
Modify your table structure and you will always know all matching records:
select
sd.station_id, sd.station_name,
sr.DataRecieved
rf.rf, rf.cum-rf,
wl.wl
from Station_Details sd
inner join Station_Records sr
on sr.station_id = sd.station_id
left join RF_Details rf
on rf.record_id = sr.record_id
left join WL_Details wl
on wl.record_id = sr.record_id

Sql Select from another table (loop?)

My SQL skills aren't great hence the post.
I'm trying to get all the contact names based on a company out.
For example I have two statements:
Select Id, CompanyName, Address From Clients
Select ClientId, ContactName From Contacts
You may have many contacts to a single client
Result: (I need all the contact names in a single column)
ContactName Company Address
----------------------------------------
Johh, Steve 123 Comp 12345 Address
David,Mike, Sarah 44 Comp 111 Address
A working example would be very much appreciated.
SELECT DISTINCT (
SELECT ISNULL(ct.ContactName, '') + ', '
FROM dbo.Clients cl JOIN dbo.Contacts ct ON cl.Id = ct.ClientId
WHERE cl.ID = cl2.Id
FOR XML PATH('')) AS ContactName, CAST(cl2.Id AS nvarchar(7)) + ' ' + cl2.CompanyName AS Company, Address
FROM dbo.Clients cl2
ORDER BY 2
Demo on SQLFiddle
Firstly build all the Contact Names for a Company into a Single Column. Assuming the database to be SQL Server, I'm using a Common Table Expression to store the single column contact list. Once the CTE is built, join it with the Clients table to get the ContactNames. FOR XML is used to concatenate rows.
WITH CTEContactList(ClientID,ContactNames)
AS
(
SELECT c1.ClientID,
Names = SUBSTRING(( SELECT ', ' + c2.ContactName
FROM Contacts c2
WHERE c1.ClientID = c2.ClientID
FOR XML PATH ('')),3,8000 ))
FROM Contacts c1
GROUP BY c1.ClientID
)
SELECT
cl.ID,
cl.CompanyName,
cl.Address,
ctelist.ContactNames
FROM Clients cl
INNER JOIN CTEContactList ctelist
ON cl.ID = cteList.ClientID
Sounds like you need to do a table join.
Example: two tables here
1. Person
2. Orders
Query:
SELECT
Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
INNER JOIN Orders ON Persons.P_Id = Orders.P_Id
ORDER BY Persons.LastName
You didn't specify your DBMS, so I'm assuming PostgreSQL:
select string_agg(ct.contactName, ', '), cl.companyname, cl.address
from contacts ct
join clients cl on cl.id = ct.clientId
group by cl.companyname, cl.address

Resources