I have two different queries
<cfquery datasource="#one#" name="GetResults">
SELECT ID, Account, Value
FROM Logger_Events
left join Event_Details
on ID = EventID
where type = '123'
</cfquery>
<cfquery datasource="#two#" name="getSubtotal">
select account from daily
</cfquery>
The first query gives me like 200 records , the second query gives me like
half a million records.
Is there a way i can make the second query get the records from the first query,
without making a query of queries?
I would be using the account column to join.
The tables are in different servers.
This is one of the reasons for a QoQ. You could run the 1st query, then convert the data to XML and pass it as a parameter to a stored proc on the 2nd DB server in order to filter the 2nd query directly on the DB. Just join to the XML or use the XML to populate a temp table.
Since you are using sql server, I suggest linked servers. Pick one of your servers and set up a linked server to the other one. Then set up a stored procedure that does something like this;
declare #TempTable table (field1 datatype1, etc);
insert into #TempTable (field1, field2, etc)
select field1, field2, etc
from openquery
(linkedServerName,
'select field1, field2 etc
from blah blah blah
')
select yourfields
from #TempTable join aTableInThatDB on etc
The details are kind of painful at first, but it works very well. Also, there is lot's of information regarding linked servers and openquery on the internet.
I have done this function to Replace the foreign key of a table for a text field contained in the foreign table. This could instead append new columns to the original query with QueryAddColumn() and QuerySetCell().
The idea is to convert one of the queries into a struct.
My function is pretty basic but at the moment looks something like this:
<cffunction name="joinNameFromIDStructs" returnType="query" hint="Returns the Fund Activity">
<cfargument name="qryMain" type="query" required="Yes" hint='The query which is going to be translated' >
<cfargument name="foreignTable" type="string" required="Yes" hint='table which contains the details of our foreign column' >
<cfargument name="foreignPK" type="string" required="No" default="#arguments.foreignTable#ID" hint="Primary Key in the foreign table" >
<cfargument name="foreignKey" type="string" required="Yes" hint="column in the main table which references another table" >
<cfargument name="foreignText" type="string" required="Yes" hint="the column in the foreing table which hold the required details. It will override the foreign key column in the main table" >
<cfset var idPK = "">
<cfset var qryForeignTable = "">
<cfset var stForeignTable = StructNew()>
<cfset var listForeignPKs = evaluate("ValueList(arguments.qryMain.#arguments.foreignKey#)")/>
<cfif ListLen(listForeignPKs)>
<cfquery name="qryForeignTable" datasource='#application.siteDataSource#' >
SELECT #arguments.foreignPK#, #arguments.foreignText# FROM #arguments.foreignTable#
WHERE #arguments.foreignPK# IN (<cfqueryparam cfsqltype="cf_sql_integer" value='#arrayToList(listToArray(listForeignPKs))#' list="true" />) <!--- CF8 does not support ListRemoveDuplicates , hence we will use the array conversion to remove empties --->
</cfquery>
<cfloop query='qryForeignTable'>
<cfset stForeignTable[qryForeignTable[arguments.foreignPK][qryForeignTable.currentrow]] = qryForeignTable[arguments.foreignText][qryForeignTable.currentrow]/>
</cfloop>
<cfloop query='arguments.qryMain'>
<cfset idPK = arguments.qryMain[arguments.foreignKey]/>
<cfif idPK neq "">
<cfset arguments.qryMain[arguments.foreignKey][arguments.qryMain.currentrow] = stForeignTable[idPK]/>
</cfif>
</cfloop>
</cfif>
<cfreturn arguments.qryMain/>
</cffunction>
Related
I want to write a function in CF, that has a query that returns multiple columns. I researched and read and could only find examples of functions with sql queries only returning the table view of the struct.
How would I write a function in CF with sql queries and output each column in text format
I been googling it and looking on tutorial websites, with no luck
<cffunction name="queryListEmployee" returntype="query" output="false">
<cfargument name="EMPID" type="numeric" default="1"/>
<cfset var listEmployee = ""/>
<cfquery name="local.listContractors" datasource="DB">
SELECT E.FIRSTNAME, E.LASTNAME
FROM EMPLOYEE E
WHERE E.ID = <cfqueryparam value="#arguments.EMPID#" cfsqltype="cf_sql_decimal" scale="0"/>
</cfquery>
<cfreturn local.listEmployee/>
I want to be able to output column names like queryListEmployee.firstname and queryListEmployee.lastname
You've got the idea right. Just a few tweaks:
<cffunction name="getEmployeeByID" returntype="query" output="false">
<cfargument name="EMPID" type="numeric" default="1"/>
<cfquery name="local.employee" datasource="DB">
SELECT E.FIRSTNAME, E.LASTNAME
FROM EMPLOYEE E
WHERE E.ID = <cfqueryparam
value="#arguments.EMPID#"
cfsqltype="cf_sql_integer"/>
</cfquery>
<cfreturn local.employee/>
</cffunction>
Then call this function like so:
<cfset qEmployee = getEmployeeByID(1)>
and output the data like this:
<cfoutput query="qEmployee">
<li>#qEmployee.FIRSTNAME# #qEmployee.LASTNAME#</li>
</cfoutput>
This function will only ever return one record. You can search and figure out how to dynamically adjust the search criteria in order to return multiple records.
You might check out http://www.learncfinaweek.com/ to get learn more of the basics of ColdFusion.
This question already has answers here:
Coldfusion - variable field name when looping through database query results
(2 answers)
Closed 7 years ago.
I want to create a function that will loop through an arbitrary query and perform an insert into another table based on the arbitrary column names.
For instance, the idea here would be to output
(data, data, data...)
(data, data, data...)
Ultimately, I'm going to do an insert query. For now, I just want to output the rows.
EDIT: I can't just do an INSERT/SELECT because the prod data and dev data are on different servers. So I have to first collect the data from the table on the prod server into a CF query object, and then loop through it and insert into the table on the dev server.
Code:
<cffunction name="copyProdToDev">
<cfargument name="devDatasource" >
<cfargument name="prodDataSource" type="string">
<cfargument name="devTableName" type="string">
<cfargument name="prodTableName" type="string">
<cfargument name="dateColumnName" default="none">
<cfquery name="ProdData" datasource="#prodDatasource#" timeout="60">
SELECT *
FROM #prodTableName#
</cfquery>
<cfset columnNames = ProdData.getColumnNames()>
<cfset numColumns = ArrayLen(columnNames)>
<cfloop query="#ProdData#">
(
<cfloop index="colNumber" from="1" to="#ArrayLen(columnNames)-1#">
<cfoutput><dynamic column name for colNumber>,</cfoutput>
</cfloop>
<cfoutput><dynamic column name for numColumns></cfoutput>
)<br />
</cfloop>
</cffunction>
Array notation is your friend.
<cfoutput>
<cfloop query = "ProdData">
<cfloop array = "#ProdData.getColumnList()#" index = columnName>
#prodData[columnName][currentrow]#
closing tags and formatting stuff
You could get the list from this variable
ProdData.columnList
But be aware that the order of the columns is not totally respected,
this article might help: http://www.bennadel.com/blog/644-query-columnlist-does-not-return-true-column-ordering.htm
How would I select a specific range of rows using a query of queries?
e.g
<cfquery name="myQuery" maxrows ="20" startrow="12">
SELECT *
FROM previous_query
WHERE row_numer >= 12
</cfquery>
that sort of thing...
This was a tricky one but your problem intrigued me. I think I may have a solution
I wrote a function that delete everything prior to the rows you want and then deletes everything after the rows you want.
the function rowrange() takes 3 parameters.
1. the queryname you are working with.
2. the starting row you want
3. the number of rows you want.
UPDATED: My friend John Whish pointed out that I actually do not need to do the looping to get this to work. Removing the loops makes this much more scalable.
<cfquery name="myQuery">
SELECT *
FROM previous_query
WHERE row_numer >= 12
</cfquery>
<cfset rowRange(myQuery,7,4)>
<cfdump var="#myQuery#">
<cffunction name="rowRange" hint="return a range of rows from a given query">
<cfargument name="qObj" type="query" required="true">
<cfargument name="start" type="numeric" required="true" default="1" hint="The number of the first row to include">
<cfargument name="range" type="numeric" required="true" default="1" hint="The number of rows">
<cfset var i = arguments.start+arguments.range-1>
<cfset arguments.qObj.removeRows(i,arguments.qObj.recordcount-i)>
<cfset arguments.qObj.removeRows(0,arguments.start-1)>
<cfreturn arguments.qObj>
</cffunction>
Doing this natively in CF isn't supported, so you'll have to add a column in your original record set to do the counting for you.
SELECT ..., row_num AS Counter
Row_Num may vary based on your DBMS.
I have two queries:
<cfquery name="getChairReview" datasource="#application.dsn#">
SELECT*
FROM eval_reviews
WHERE faculty = <cfqueryparam cfsqltype="cf_sql_numeric" value="#HTMLEditFormat(trim(arguments.id))#">
AND evalYear = <cfqueryparam cfsqltype="cf_sql_numeric" value="#HTMLEditFormat(trim(arguments.evalYear))#">
AND levels = <cfqueryparam cfsqltype="cf_sql_varchar" value="chair">
</cfquery>
<cfreturn getChairReview>
</cffunction>
<cffunction name="getDeanReview" access="public" returntype="query">
<cfargument name="id" type="numeric" required="yes">
<cfargument name="evalYear" type="numeric" required="yes">
<cfset var getDeanReview = QueryNew("")>
<cfquery name="getDeanReview" datasource="#application.dsn#">
SELECT*
FROM eval_reviews
WHERE faculty = <cfqueryparam cfsqltype="cf_sql_numeric" value="#HTMLEditFormat(trim(arguments.id))#">
AND evalYear = <cfqueryparam cfsqltype="cf_sql_numeric" value="#HTMLEditFormat(trim(arguments.evalYear))#">
AND levels = 'dean'
</cfquery>
<cfreturn getDeanReview>
</cffunction>
Both of them work fine when evalYear is 2012. If I send in 2011 I get an error on getDeanReview that states: [Macromedia][SQLServer JDBC Driver][SQLServer]Error converting data type varchar to numeric
The line it gives this error on is the AND levels = 'dean' (note: I had taken out the cfqueryparam tag to make sure I didn't goof that up.)
I find this strange because these two queries are almost identical except one has chair as a param and the other dean. (Yes, I could have had one function to do the job...with extra param for level)
I changed 'dean' to 'chair' with evalYear still 2011...guess what? It worked. This is quite odd...
levels is a varchar field.
Any ideas?
That's weird.
Okay, for grits & shins, try making the second query:
and levels like '%dean%'
Also, try it the way you have it, but remove the line about year and let it select everything regardless of year... see what your output looks like.
Lastly.. I don't think you need to worry about HTMLEditFormat in your value params in cfqueryparam.
Rob
I want to get a specific row in a ColdFusion Query object without looping over it.
I'd like to do something like this:
<cfquery name="QueryName" datasource="ds">
SELECT *
FROM tablename
</cfquery>
<cfset x = QueryName[5]>
But it's giving me an error saying that the query isn't indexable by "5". I know for a fact that there are more than 5 records in this query.
You can't get a row in CF <= 10. You have to get a specific column.
<cfset x = QueryName.columnName[5]>
It's been 8 years since I posted this answer, however. Apparently CF11 finally implemented that feature. See this answer.
This can now be accomplished in coldfusion 11 via QueryGetRow
<cfquery name="myQuery" result="myresult" datasource="artGallery" fetchclientinfo="yes" >
select * from art where ARTID >
<cfqueryparam value="2" cfsqltype="CF_SQL_INTEGER">
</cfquery>
<cfdump var="#myQuery#" >
<cfset data = QueryGetRow(myQuery, 1) >
<cfdump var="#data#" >
I think there is a simpler solution...
I am guessing you know your column names and only want this column or that one. Then you don't need to put the whole row in a struct. You can reference the query by row number (remember its 1 based not 0).
yourQueryName["yourColumnName"][rowNumber]
<cfoutput>
#mycontacts["Name"][13]#
#mycontacts["HomePhone"][13]#
</cfoutput>
You have to convert the query to a struct first:
<cfscript>
function GetQueryRow(query, rowNumber) {
var i = 0;
var rowData = StructNew();
var cols = ListToArray(query.columnList);
for (i = 1; i lte ArrayLen(cols); i = i + 1) {
rowData[cols[i]] = query[cols[i]][rowNumber];
}
return rowData;
}
</cfscript>
<cfoutput query="yourQuery">
<cfset theCurrentRow = GetQueryRow(yourQuery, currentRow)>
<cfdump var="#theCurrentRow#">
</cfoutput>
Hope this points you in the right direction.
I know I come back to this thread any time I Google "cfquery bracket notation". Here's a function I wrote to handle this case using bracket notation. Hopefully this can help someone else too:
<cffunction name="QueryGetRow" access="public" returntype="array" hint="I return the specified row's data as an array in the correct order">
<cfargument name="query" required="true" type="query" hint="I am the query whose row data you want">
<cfargument name="rowNumber" required="true" hint="This is the row number of the row whose data you want">
<cfset returnArray = []>
<cfset valueArray = []>
<cfset cList = ListToArray(query.ColumnList)>
<cfloop from="1" to="#ArrayLen(cList)#" index="i">
<cfset row = query["#cList[i]#"][rowNumber]>
<cfset row = REReplace(row, "(,)", " ")>
<cfset returnArray[i] = row>
<cfset i++>
</cfloop>
<cfreturn returnArray>
</cffunction>
The REReplace is optional, I have it in there to cleanse commas so that it doesn't screw up the arrayToList function later on if you have to use it.
I wanted to extract a single row from a query, and keeping the column names (of course). This is how I solved it:
<cffunction name="getQueryRow" returntype="query" output="no">
<cfargument name="qry" type="query" required="yes">
<cfargument name="row" type="numeric" required="yes">
<cfset arguments.qryRow=QueryNew(arguments.qry.columnlist)>
<cfset QueryAddRow(arguments.qryRow)>
<cfloop list="#arguments.qry.columnlist#" index="arguments.column">
<cfset QuerySetCell(arguments.qryRow,arguments.column,Evaluate("arguments.qry.#arguments.column#[arguments.row]"))>
</cfloop>
<cfreturn arguments.qryRow>
</cffunction>
Methods previously described for obtaining query data by column name and row number (variables.myquery["columnName"][rowNumber]) are correct, but not convenient for getting a full row of query data.
I'm running Railo 4.1. And this is a cool solution. Too bad this can't be done the way we would want outright to get a full row of data, but the following method allows us to get what we want through a few hoops.
When you serializeJSON(variables.myquery) it changes the query to a JSON formatted cfml struct object with two items: "Columns" and "Data". Both of these are arrays of data. The "data" array is a two-dimensional array for rows and then columnar data.
The issue is that now we have an unusable string. Then if we re-serialize it it's NOT a query, but rather usable regular struct in the format described above.
Assume we already have a query variable named 'variables.myquery'. Then look at the following code:
<cfset variables.myqueryobj = deserializeJSON(serializeJSON(variables.myquery)) />
Now you get the two dimensional array by getting this:
<cfset variables.allrowsarray = variables.myqueryobj.data />
And you get one query row array by getting this:
<cfset variables.allrowsarray = variables.myqueryobj.data[1] />
OR the last row this way:
<cfset variables.allrowsarray = variables.myqueryobj.data[variables.myquery.recordCount] />
And you can get individual column values by column order number iteration:
<cfset variables.allrowsarray = variables.myqueryobj.data[1][1] />
Now this might be slow and possibly unwise with large query results, but this is a cool solution nonetheless.
Check out the documentation for queryGetRow. It accepts a query object and an index of the row with the first row being referenced with the index of 1 (NOT 0) The index used this way is required to be a positive integer.
<cfquery name="QueryName" datasource="ds">
SELECT *
FROM tablename
</cfquery>
<!---
This would retrieve the first record of the query
and store the record in a struct format in the variable 'x'.
--->
<cfset x = queryGetRow(QueryName, 1) />
<!---
This is an alternative using the member method form of queryGetRow
--->
<cfset x = QueryName.getRow(1) />